文章目录
很伤心,为什么NOIP不能用 C++11……
CodeForces 510D Fox And Jumping:510D
Problem
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 and cost . If she pays dollars then she can apply i-th card. After applying i-th card she becomes able to make jumps of length , i. e. from cell to cell or cell .
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 , number of cards.
The second line contains n numbers , the jump lengths of cards.
The third line contains n numbers , 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 #1
3
100 99 9900
1 1 1
Output #1
2
Input #2
5
10 20 30 40 50
1 1 1 1 1
Output #2
-1
Input #3
8
4264 4921 6321 6984 2316 8432 6120 1026
4264 4921 6321 6984 2316 8432 6120 1026
Output #3
7237
Translation
题目大意是:Fox 现在在一个一维数轴上,给定 N 张卡片,选每张卡分别要付 的钱,可以让 Fox 有走 长度的技能。问你最少要多少钱买卡片可以让 Fox 抵达数轴上每个地方。
Solution
显然要通过“某种操作”组合出 1,有 1 就可以铺满数轴了,因为 1 是所有正整数的因子。那么对于两张卡 i 和 j ,都购买以后可以组合出的最小单位就是 。所以这题就转化成:
从 N 个数字里选择一部分,使得它们的 最大公因数为 1,并且 最小。
这样的题目我们很容易令我们想到DP:F[i][j][k]
表示前 i 个数字里选择了 j 个,并且它们的最大公因数是 k。很快我们就发现了问题:题目里说 ……这样数组无论如何存不下的……但是!!!可以证明最多只有 个最大公因数!那么我们完全可以用 map 进行离散。但是这样内存好像还是会爆炸……推出转移方程以后发现:当前状态只与 i-1 的状态有关!那么可以用滚动数组。内存就优化下来了~~
思考一下,很容易得出转移方程。只要注意几个细节就可以了。(代码如下)
Code
#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
using namespace std;
const int maxn=305;
int n,a[maxn],p[maxn],to[maxn*maxn],f[2][maxn][maxn*maxn],cnt=0,ans,INF;
map <int,int> back; // 为了压缩内存,建立映射关系 (离散)
inline int read(){
int ret=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
inline int gcd(int x,int y){
if (y==0) return x;
return gcd(y,x%y);
}
int main(){
n=read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=n;i++) p[i]=read();
memset(f,63,sizeof(f));INF=f[0][0][0];
cnt++;to[cnt]=0;back[0]=cnt; // 建立基本映射单位的过程
f[0][0][1]=0;
for (int i=1;i<=n;i++)
for (int j=0;j<=i;j++){
int oldcnt=cnt;
for (int k=1;k<=oldcnt;k++){
if (f[1-(i&1)][j][k]<f[i&1][j][k]) f[i&1][j][k]=f[1-(i&1)][j][k];
if (j==0||f[1-(i&1)][j-1][k]==INF) continue;
int now=gcd(to[k],a[i]);
if (back[now]==0) {cnt++;to[cnt]=now;back[now]=cnt;}
int nowx=back[now];
if (f[1-(i&1)][j-1][k]+p[i] < f[i&1][j][nowx]) f[i&1][j][nowx]=f[1-(i&1)][j-1][k]+p[i];
}
}
ans=INF;int st=back[1];
if (st) for (int i=1;i<=n;i++) if (f[n&1][i][st]<ans) ans=f[n&1][i][st];
if (ans-INF) printf("%d\n",ans); else printf("-1\n");
return 0;
}
自豪地,完全自己想出来的~~~