CodeForces - 510D Fox And Jumping (dp & 数论)

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 li and cost ci. If she pays ci dollars then she can apply i-th card. After applying i-th card she becomes able to make jumps of length li, i. e. from cell x to cell (x - li) or cell (x + li).
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.
Input
The first line contains an integer n (1 ≤ n ≤ 300), number of cards.
The second line contains n numbers li (1 ≤ li ≤ 109), the jump lengths of cards.
The third line contains n numbers ci (1 ≤ ci ≤ 105), the costs of cards.
Output
If it is impossible to buy some cards and become able to jump to any cell, output -1. Otherwise output the minimal cost of buying such set of cards.
Examples
Input

3
100 99 9900
1 1 1
Output
2
Input
5
10 20 30 40 50
1 1 1 1 1
Output
-1
Input
7
15015 10010 6006 4290 2730 2310 1
1 1 1 1 1 1 10
Output
6
Input
8
4264 4921 6321 6984 2316 8432 6120 1026
4264 4921 6321 6984 2316 8432 6120 1026
Output
7237
Note
In first sample test, buying one card is not enough: for example, if you buy a card with length 100, you can’t jump to any cell whose index is not a multiple of 100. The best way is to buy first and second card, that will make you be able to jump to any cell.
In the second sample test, even if you buy all cards, you can’t jump to any cell whose index is not a multiple of 10, so you should output -1.
题目链接
参考题解
题目大意:有个无限长的磁带,一开始处于位置0.有若干张卡片,每张卡片有长度和代价.购买一张卡片后可以无限使用,前进或后退长度的步数,问如何用最小的代价购买若干张卡片,使得可以到达磁带的任意位置.
思路:这个要想整个长度全都能够遍历下来的话,那么必须要找到几个长度使得他们的线性组合能够是 1 ,这样才能将整条磁带都遍历下来,否则,只要有空隙,那么就一定是有空隙无法触及的。
这时候就要结合数论了,因为有:对任意的a, b总存在s,t使得sa + tb = gcd(a, b),对于多个参数以此类推。于是,我们就可以想到,要从这些给出数据中选出价值最小的组合使得他们的最大公因数是1。
剩下的部分就是动态规划了,和背包的思想差不多,对于每个数字就是放或者不放。对于每一个数遍历以前生成的最大公因数,然后生成新的最大公因数,首先判断这个数以前有没有生成,生成过的话,就看以前和现在那种花费少,如果没有生成过,那么就直接记录。
代码:emmm,没有用过map类的动态规划写法,以后可以尝试一下,感觉还不错,代码量也很少,还有的优点借用原作者的话:“这个代码中的迭代器是不会失效的,我们可以发现map迭代器在增加元素的时候并不会失效.并且同方向遍历的话,还能遍历到新添加的元素.利用这个特点写的gcd dp代码特别短。”

可能说的不清楚,结合代码理解一下就懂了

#include 
#include 
#include 
using namespace std;
const int MAX_N = 3e2 + 5;
int n, l[MAX_N], c[MAX_N];
map mp;
typedef map::iterator Mpit;   //map的迭代器的first是键值,second是存的数值

int gcd(int a, int b)   //求最大公约数
{
    return !b ? a : gcd(b, a % b);
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) scanf("%d", &l[i]);
    for(int i = 1; i <= n; i++) scanf("%d", &c[i]);
    mp[0] = 0;
    for(int i = 1; i <= n; i++) //遍历每一个卡片能够提供的长度
    {
    	//遍历之前已经求出的所有最小公约数的组合
        for(Mpit it = mp.begin(); it != mp.end(); it++) 
        {
            int _gcd = gcd(it->first, l[i]);    //与之前的最大公因数求最大公因数
            //看这个最大公因数以前有没有出现过,如果出现过,
            //那么从现在这种方式和以前的方法中选一种花费最少的,
            //如果没有出现过,那么直接记录
            mp.count(_gcd) ? mp[_gcd] = min(mp[_gcd], it->second + c[i]) : mp[_gcd] = it->second + c[i];
        }
    }
    cout << (mp[1] ? mp[1] : -1) << endl;   //看最大公约数能不能是1,如果是1,那就一定能遍历所有长度,没有的话直接输出-1
    return 0;
}

你可能感兴趣的:(数论,动态规划)