下文讲的解都是整数解
裴蜀定理和扩展欧几里得算法
裴蜀恒等式:方程$ax+by=gcd(a,b)$存在一组特解$x=x0,y=y0$。
证明:
其实我们可以通过欧几里得算法来证明。
设$gcd(a,b)=g$。
欧几里得算法的最后一步会得到两个数$g, 0$。
然后令$x0=1,y0=0$,就得到了一组解$g \times 1 + 0 \times 0 = g$。
假设当前我们要求一组解满足$ax+by=g$,且我们已经得到了一组解满足$bx0+(a-[a/b]*b)y0=g$。
拆开来得到:$ay0+b(x0-[a/b]*y0)=g$,所以我们可以令$x1=y0, y1=x0-[a/b]*b*y0$而得到一组解$x1,y1$。
一直这么推下去就可以得到最后的答案。
证毕。
扩展欧几里得算法:求$ax+by=gcd(a,b)$的一组通解。
要想求通解求出一组特解(x0,y0)即可。
通解可表示为$x=x0+\frac{b}{g}, y=y0-\frac{a}{g}$,即两边同时减去最小公倍数。
要求特解我们发现就是刚才裴蜀恒等式的证明。
代码:
void exgcd(int a, int b, int &x, int &y) { if (b == 0) { x = 1; y = 0; return a; } int g = gcd(b, a % b, x, y); int tmp = y; x = y; y = x - a / b * y; }
求解一般不定方程:
给定一个不定方程$ax+by=c$,求其通解。
容易发现如果$gcd(a,b)$不整除c则无解。
如果整除可以先算出一组方程$ax+by=gcd(a,b)$的特解$x0,y0$,然后再乘上$\frac{c}{d}$。
思路:
根据裴蜀定理,ax+by能表示出所有数当且仅当$(a,b)=1$。不难知道扩展到n项也是成立的。
所以这变成了一个背包问题,设$dp_i$代表选出的数的最大公约数为$i$的最小价值。最后的答案为$dp_1$。
我们可以通过$dp_j$来更新$dp_{gcd(j,c[i])}$。
但gcd可能很大,不能直接枚举。所以我们可以用平衡树把所有已经可能出现的gcd存下来,然后用其来更新。
注意自己也是可以单独的一个gcd的,这是初始化。
#include <bits/stdc++.h> using namespace std; int n, l[310], c[310]; map<int, int> mp; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &l[i]); for (int i = 1; i <= n; i++) scanf("%d", &c[i]); mp[l[1]] = c[1]; for (int i = 2; i <= n; i++) { if (mp.find(l[i]) == mp.end()) mp[l[i]] = c[i]; else mp[l[i]] = min(mp[l[i]], c[i]); for (map<int, int>::iterator it = mp.begin(); it != mp.end(); it++) { int val = it -> first; val = __gcd(val, l[i]); if (mp.find(val) == mp.end()) mp[val] = mp[it -> first] + c[i]; else mp[val] = min(mp[val], mp[it -> first] + c[i]); } } if (mp.find(1) != mp.end()) printf("%d", mp[1]); else puts("-1"); return 0; }