---
title: Fox and Jumping
tags:
  - Dynamic Programming
  - GCD-LCM
id: "665"
categories:
  - [OI, Common Skill]
  - [OI, Number Theory]
date: 2017-06-24 01:00:14
---

## 题目描述

Fox Ciel is playing a game. In this game there is an infinite long tape with cells indexed by integers (positive, negative and zero). At the beginning she is standing at the cell $0$.

There are also $n$ cards, each card has $2$ attributes: length $l_i$ and cost $c_i$. If she pays $c_i$ dollars then she can apply $i$-th card. After applying $i$-th card she becomes able to make jumps of length $l_i$, i.e. from cell $x$ to cell $(x-l_i)$ or cell $(x+l_i)$.

She wants to be able to jump to any cell on the tape (possibly, visiting some intermediate cells). For achieving this goal, she wants to buy some cards, paying as little money as possible.

If this is possible, calculate the minimal cost.

## 题意概述

有$n$张卡片，每张卡片都有两个属性$l_i, c_i$。你需要从中选择一些卡片，使得在满足用已选卡片的$l_i$任意加减可以得到所有整数的情况下，已选卡片的$c_i$之和最小。求出这个最小值。

数据范围：$1 \le n \le 300, \; 1 \le l_i \le 10^9, \; 1 \le c_i \le 10^5$。

## 算法分析

可以发现，如果所有已选卡片的$l_i$的最大公约数为$1$，则满足要求。

用$f_k$表示已选卡片的$l_i$的最大公约数为$k$时$c_i$之和的最小值。有如下转移方程：

$$
f_{(k, l_i)}=\min(f_k+c_i)
$$

如果最后$f_1$不存在，则说明无解。

由于$k$可能会很大，但个数不会很多，可以使用 map 来节省空间。

## 代码

```cpp
#include <iostream>
#include <map>
using namespace std;
int n, l[301], c[301];
map<int, int> f;
int gcd(int a, int b) {
    return b ? gcd(b, a % b) : a;
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> l[i];
    }
    for (int i = 1; i <= n; ++i) {
        cin >> c[i];
    }
    f[0] = 0;
    for (int i = 1; i <= n; ++i) {
        for (map<int, int>::iterator iter = f.begin(); iter != f.end(); ++iter) {
            int t = gcd(iter->first, l[i]);
            if (!f.count(t)) f[t] = iter->second + c[i];
            else f[t] = min(f[t], iter->second + c[i]);
        }
    }
    if (!f[1]) cout << -1 << endl;
    else cout << f[1] << endl;
    return 0;
}
```
