SkyWT / 博客 / CodeForces 510D Fox And Jumping:DP + 数论 + 离散

CodeForces 510D Fox And Jumping:DP + 数论 + 离散

2018 年 7 月 10 日 13:19


文章目录

很伤心,为什么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 lil_i and cost cic_i. If she pays cic_i dollars then she can apply i-th card. After applying i-th card she becomes able to make jumps of length lil_i, i. e. from cell xx to cell (xli)(x - l_i) or cell (x+li)(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.

Input

The first line contains an integer n (1n300)(1 ≤ n ≤ 300), number of cards.

The second line contains n numbers lil_i (1li109)(1 ≤ l_i ≤ 10^9), the jump lengths of cards.

The third line contains n numbers cic_i (1ci105)(1 ≤ c_i ≤ 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 #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 张卡片,选每张卡分别要付 PiP_i 的钱,可以让 Fox 有走 LiL_i 长度的技能。问你最少要多少钱买卡片可以让 Fox 抵达数轴上每个地方。

Solution

显然要通过“某种操作”组合出 1,有 1 就可以铺满数轴了,因为 1 是所有正整数的因子。那么对于两张卡 i 和 j ,都购买以后可以组合出的最小单位就是 gcd(Li,Lj)gcd(L_i,L_j) 。所以这题就转化成:
从 N 个数字里选择一部分,使得它们的 LiL_i 最大公因数为 1,并且 Pi\sum P_i 最小。

这样的题目我们很容易令我们想到DP:F[i][j][k] 表示前 i 个数字里选择了 j 个,并且它们的最大公因数是 k。很快我们就发现了问题:题目里说 (1li109)(1 ≤ l_i ≤ 10^9) ……这样数组无论如何存不下的……但是!!!可以证明最多只有 N2N^2 个最大公因数!那么我们完全可以用 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;
}

自豪地,完全自己想出来的~~~


暂无评论


发表新的评论

所有评论都将经过博主审核。请勿填写无意义邮箱或发表无关评论、广告等,否则会被视为垃圾评论。

提交评论即表明你同意本网站使用 Cookie,并允许本站在后台记录你的邮箱、IP 地址等必要信息。
(提交一次评论后,本提示将不再展示)