Codeforces 510D Fox And Jumping【数论+DP】

题目链接:http://codeforces.com/problemset/problem/510/D

题目大意:
给你N个数,每个数用的话需要花费ci,如果他站在x位子,选取了ai,那么他可以选择走到(x+ai)或者是(x-ai),走的次数不限。问一开始他站在原点,能否通过选取一些数字使得他能够走到任意位子。

思路:
其实如果能够组成1的话,就能组成所有任意整数。最终我们也就是需要找出拼凑1的最小花费。

关于怎么拼凑1还是听完题解后才知道的:数论中有个小东西叫裴蜀定理,根据它我们可以知道两个数可以组成的最小的的数就是他们的最大公约数gcd(同样是学了扩欧的人。。。。)。所以我们就要找出哪几个数的最大公倍数为1,且为最小花费。

因为题目中n<300,每个数的因子个数最多log2n,所以直接暴力写dp过去存下来的gcd值也不会特别多。但是这里它给的n个数的数据范围比较大,有1e9,所以可以用map来存映射关系。

感觉很多时候题目中数据范围比较大不能开数组时,map的用处就来了:(最近学dp还遇到过一个用一用到map的题:bzoj 3679: 数字之积)

代码:

#include<iostream>
#include<cstdio>
#include<string.h>
#include<map>
#include<algorithm>
using namespace std;
typedef long long LL;
const int manx=2e4+10;
const LL INF=1e15;
//const LL INF=0x3fffffff;
map<LL,LL>dp;//dp[i]表示最大公约数为i四所需的最小花费
int a[manx];
int main()
{
    int n;
    LL l[400],c[400];
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&l[i]);
    for(int i=1;i<=n;i++)
        scanf("%lld",&c[i]);
    map<LL,LL>::iterator it;
    for(int i=1;i<=n;i++)
    {
        //int l=0;
        for(it=dp.begin();it!=dp.end();it++)
        {
            if(!(it->second))
            {
                //dp.erase(it);
                continue;
            }
            LL net=__gcd(it->first,l[i]);
            if(dp[net])
                dp[net]=min(dp[net],it->second+c[i]);
            else
                dp[net]=it->second+c[i];
            //printf("%d\n",dp[net]);
        }
        if(dp[l[i]])
            dp[l[i]]=min(dp[l[i]],c[i]);
        else dp[l[i]]=c[i];
    }
    if(dp[1])
        printf("%lld\n",dp[1]);
    else printf("-1\n");
}

发布了52 篇原创文章 · 获赞 26 · 访问量 3158

猜你喜欢

转载自blog.csdn.net/qq_43803508/article/details/103948742
Fox