CF510D(裴蜀定理)

题意:

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

思路:

首先分析子问题,先考虑两个数的情况,因为纸带是无限长的,没有循环,我们发现,要想能够跳到每一个格子上,就必须使得我们选择的数通过数次加减得到的数的绝对值为 1,进而想到了裴蜀定理。

裴蜀定理的内容是:设a,b 是不全为零的整数,则存在整数 x,y,使得 ax+by=gcd(a,b)。 我们想要解这个二元一次方程得到答案 1,也就是使得 gcd(a,b)=1,我们可以推出:如果 a 与b 互质,则一定存在两个整数 x,y ,使得 ax+by=1。

由此我们想到本题的解题思路:选择最便宜的两个或者多个互质的数(多个数互质是指
gcd(gcd(gcd(a,b)c...)=1)

至此整体的思路已经非常清晰了:我们从 0 号节点开始,每一步走到的结点编号为 gcd(x,y) ,其中 x 为当前的结点编号,也就是当前已选择的数的总 gcd,y 为我们枚举到的下一个结点 ,也就是下一个待选的数的值 l [ i ],与此同时更新代价,利用 dijkstra 算法求得最小的总代价。

最后如果能够到达结点 1,也就意味着我们能够找到若干个数使得他们的总 gcd 为 1 ,输出
dist[1] 即可。反之说明无法得到,即无法遍历所有的格点。

最后用 map 存数找数即可。

AC代码:

// -----------------
//#pragma GCC optimize(2)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(0);
#define fixed fixed<<setprecision
#define endl '\n'
#define inf 0x3f3f3f3f
#define PII pair<int,int>
#define rep(i,x,y) for(int i = (x); i <= (y); i ++)
#define dec(i,y,x) for(int i = (y); i >= (x); i --)

using namespace std;

const int N = 1e6 + 7;
int l[N],c[N];
int n;
map<int,int> st,dist;

void dij() 
{
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, 0});
    dist[0] = 0;
    
    while (heap.size())
    {
        auto t = heap.top().second;
        heap.pop();
        if(t == 1) break;
        
        if (st.find(t) != st.end()) continue;
        st[t] = true;

        for (int i = 1; i <= n; i ++)
        {
            int y = __gcd(t, l[i]);
            if(dist.find(y) == dist.end()) dist[y] = inf;
            if(dist[y] > dist[t] + c[i])
            {
                dist[y] = dist[t] + c[i];
                heap.push({dist[y], y});
            }
        }
    }
}

void solve()
{
    cin >> n;
    rep(i,1,n) cin >> l[i];
    rep(i,1,n) cin >> c[i];
    
    dij();
    
    if(dist.find(1) == dist.end()) cout << -1 << endl;
    else cout << dist[1] << endl;
}

signed main()
{
    IOS 
    int T = 1;
    //cin >> T;
    while(T --) { solve(); }
    return 0;
}
posted @ 2023-07-13 10:01  陌上&初安  阅读(93)  评论(0)    收藏  举报