codeforces290 B.Fox And Jumping

来源:互联网 发布:python编写远程控制 编辑:程序博客网 时间:2024/06/13 23:55

思考过程:

哪些数字组合起来可以达到每一步都能走到呢?

若一开始是在0号位,那么能走到1号位置就能达到每一步。

那哪些数字组合在一起可以达到1呢?

由数论知识可知

存在a、b,使a*x + b*y = gcd(x, y)

于是可以得到,存在a1、a2.......、an-1、an,使a1*x1 + a2*x2 + ....... + an-1*xn-1 + an*xn = gcd(x1, x2, x3 ,....., xn-1, xn)  

(这一点可以由上面一点证明)

现在我们就是要用最小的花费找到一组最大公约数为 1 的一组数据。

这种情况就三种算法 贪心、动态规划、 网络流。

先想的是动态规划。最先的想法是,f[i, j],表示选到了第 i 个数,能选取一组数的最大公约数为 j 的最小花费。

但这样我无论状态数和时间花费我都压不下去。

后面我又想了很久贪心,不得其方法。

最后我还是把算法聚焦在动态规划上面。从最大公约数的性质上面下手。

一组数的最大公约数为1时,必定不存在一个质数能够整除这一组的所有数。

由此得到算法:

先枚举选取一个数,然后提取出可以整除这个数的质数。

可知,这些质数的个数不超过10,因为2*3*5*7*11*13*17*19*23*29 > 10^9

所以这一步的时间复杂度是O( sqrt(10^9) )

然后对这些质数进行状态压缩动规,f[i] 表示 若i and 2^(j) = 0 则 使第 j 个质数不能整除所有选取数的最小花费。

所以f[0] 就是选取的数最大公约数为 1 的最小花费

状态数是2^10 = 1000

要扫过n个数,所以这一步的时间复杂度是O(1000*300)

因为要枚举n个数来进行这个过程。所以整个算法的时间复杂度是O(300*sqrt(10^9) + 300*300*1000) = 9*10^7 = 10^8

我的代码

#include <iostream>#include <stdio.h>#include <stdlib.h>#include <algorithm>#include <cmath>using namespace std;const int MAX = 1e9;int l[1000],c[1000];int f[10000];int a[20];int o[20];int main(){int n,i,j,k,ans,m,t,p;o[0]=1;for (i=1; i<20; i++)o[i]=o[i-1]*2;cin >> n;for (i=0; i<n; i++)cin >> l[i];for (i=0; i<n; i++)cin >> c[i];ans = MAX;for (i=0; i<n; i++)  //枚举选取一个数 {t = 0;p = l[i];//找出能够整除这个数的质数 for (j=2;j<sqrt(n);j++)if (p%j == 0){a[t++] = j;while (p%j == 0)p /= j;}if (p!=1)a[t++]=j;m = o[t]-1;//状态压缩动规初始化 for (j=0; j<m; j++)f[j] = MAX;f[m] = c[i];//状态压缩动规 for (j=0; j<n; j++){p = 0;for (k=0; k<t; k++)if (l[j] % a[k] == 0)p += o[k];for (k=m; k>=0; k--)if (f[k] + c[j] < f[k && p])f[k && p] = f[k] + c[j];}//比较花费大小,选取更小花费 if (f[0]<ans)ans = f[0];}//输出答案 if (ans == MAX)cout << -1 << endl;else cout << ans << endl;return 0;}


0 0
原创粉丝点击