出处: Codeforces
主要算法:map+DP
难度:4.6
思路分析:
题意:给出n张卡片,分别有l[i]和c[i]。在一条无限长的纸带上,你可以选择花c[i]的钱来购买卡片i,从此以后可以向左或向右条l[i]个单位。购买其他卡片后,可以获得更多的跳跃单位。先要求至少花多少元钱才能够任意跳到纸带上任意一个位置。若不行,输出-1.
首先分析如果只有两个技能的情况。若这两个技能的跳跃长度有最大公约数(x),且满足(x > 1),则一定能跳到任意一个位置。比如(x = 2),那么所有奇数的格子都是跳不到的。如果(x = 3),那么所有非3的倍数都是跳不到的。因此我们可以得到结论,当且仅当(x = 1)时才能够跳到所有的地方。
联系到多种技能的情况,若所有技能的跳跃长度的最大公约数大于1,那么就像刚才那样一定有格子跳不到。因此要求所选技能的最大公约数必须为1。因此题目可以转化为从n个技能中选取几个,使得其最大公约数为1,并且要让花费尽量小。
这就可以联系到dp了。令f[i]表示选择一些数并且最大公约数为i时的最小花费。很明显答案就是f[1]。转移也很简单,先扫描1~n,在扫描所有可能的最大公约数j。求出j与l[i]的最小公约数tmp。利用f[j]就可以转移f[tmp]了。(用与不用f[i])
然而第9个点RE了。
回想一下过程,由于(l[i] <= 10^9),所以f数组很明显装不下了。可以n只有300,300个卡片能有多少个最大公约数啊。于是我们联想到了map,把f改成一个map就解决问题了。
代码注意点:
注意f数组的初始化,f[0]=0,不然就永远进不去循环了……
Code
/** This Program is written by QiXingZhi **/ #include <cstdio> #include <map> #include <queue> #include <cstring> #include <algorithm> #define r read() #define Max(a,b) (((a)>(b)) ? (a) : (b)) #define Min(a,b) (((a)<(b)) ? (a) : (b)) using namespace std; typedef long long ll; const int N = 310; const int INF = 1061109567; inline int read(){ int x = 0; int w = 1; register int c = getchar(); while(c ^ '-' && (c < '0' || c > '9')) c = getchar(); if(c == '-') w = -1, c = getchar(); while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar(); return x * w; } int n,tmp; int L[N],c[N]; map <int, int> f; int Gcd(int a, int b){ if(a < b) return Gcd(b,a); if(b == 0) return a; return Gcd(b,a % b); } int main(){ // freopen(".in","r",stdin); n = r; for(int i = 1; i <= n; ++i){ L[i] = r; } for(int i = 1; i <= n; ++i){ c[i] = r; } f[0] = 0; for(int i = 1; i <= n; ++i){ map <int,int> :: iterator it = f.begin(); for(; it != f.end(); ++it){ tmp = Gcd(L[i], it->first); if(f[tmp] != 0){ f[tmp] = Min(f[tmp], it->second + c[i]); } else{ f[tmp] = it->second + c[i]; } } } if(f.count(1)){ printf("%d",f[1]); } else{ printf("-1"); } return 0; }