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;
}

浙公网安备 33010602011771号