CodeForces 510D Fox And Jumping:DP+数论+离散(map 的奇技淫巧)

/ 1评 /

很伤心,为什么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 l_i and cost c_i. If she pays c_i dollars then she can apply i-th card. After applying i-th card she becomes able to make jumps of length l_i, i. e. from cell x to cell (x - l_i) or cell (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 (1 ≤ n ≤ 300), number of cards.

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

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

Solution

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

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

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


知识共享许可协议 本文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
欢迎转载,如有错误欢迎指出。
本文链接:https://skywt.cn/posts/cf510d/


一条回应:“CodeForces 510D Fox And Jumping:DP+数论+离散(map 的奇技淫巧)”

  1. ZigZagK说道:

    都C++11了当然上unordered_map啦。

发表评论

电子邮件地址不会被公开。 必填项已用*标注