数论——裴蜀定理

裴蜀定理

设 a,b是不全为零的整数,则存在整数 x,y, 使得 a*x+b*y=gcd(a,b) .

证明过程略

应用

给出 n张卡片,分别有li和ci。在一条无限长的纸带上,你可以选择花ci 的钱来购买卡片i,从此以后可以向左或向右跳li个单位。问你至少花多少元钱才能够跳到纸带上全部位置。若不行,输出-1

解析:

分析该问题,先考虑两个数的情况,发现想要跳到每一个格子上,必须使得这些数通过数次相加或相加得出的绝对值为1 ,进而想到了裴蜀定理。

可以推出:如果a与b互质,那么一定存在两个整数x与y,使得ax+by=1 .

由此得出了若选择的卡牌的数通过数次相加或相减得出的绝对值为1,那么这些数一定互质,此时可以考虑动态规划求解。

不过可以转移思想,因为这些数互质,即为0号节点开始,每走一步求 gcd(节点号,下一个节点),同时记录代价,就成为了从0 通过不断gcd最后变为1的最小代价。

由于:互质即为最大公因数为1,gcd(0,x)=x 这两个定理,可以证明该算法的正确。选择优先队列优化 Dijkstra 求解。

扫描二维码关注公众号,回复: 9739260 查看本文章

不过还有个问题,即为需要记录是否已经买过一个卡片,开数组标记由于数据范围达到 10^9会超出内存限制,可以想到使用 unordered_map 

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath> 
#include<map>
#include<queue>
#include<string> 
#include<iostream>
#include<stack>
#define ll long long
#define inf 0x3f3f3f3f
template <class T> inline void gmax(T &a,T b){if(b>a)a=b;}
template <class T> inline void gmin(T &a,T b){if(b<a)a=b;}
using namespace std;
const int N=303,M=0,Z=1e9+7,maxint=2147483647,ms31=522133279,ms63=1061109567,ms127=2139062143;
const double eps=1e-8,PI=acos(-1.0);//.0
map<int,int>mop;
map<int,int>::iterator it;
int l[N],c[N];
int n;
int gcd(int x,int y)
{
    int z;
    while(y)
    {
        z=x%y;
        x=y;
        y=z;
    }
    return x;
}
int main()
{
    while(~scanf("%d",&n))
    {
        mop.clear();
        for(int i=1;i<=n;i++)scanf("%d",&l[i]);
        for(int i=1;i<=n;i++)scanf("%d",&c[i]);
        for(int i=1;i<=n;i++)
        {
            for(it=mop.begin();it!=mop.end();it++)
            {
                int x=it->first;
                int y=it->second;
                int g=gcd(l[i],x);
                if(mop.find(g)==mop.end())mop[g]=y+c[i];
                else gmin(mop[g],y+c[i]);
            }
            if(mop.find(l[i])==mop.end())mop[l[i]]=c[i];
            else gmin(mop[l[i]],c[i]);
        }
        if(mop.find(1)==mop.end())printf("-1\n");
        else printf("%d\n",mop[1]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/2462478392Lee/p/12459710.html
今日推荐