题目描述:
给定一个长度为 \(n\) 的数列,每次操作你可以将数列中的一个数字加上一个整数 \(t\),其中 \(t\) 必须满足 \(|t| = a\) 或 \(|t| = b\)。
你需要将所有数全部变为 \(0\),无解输出 \(-1\),否则输出最小操作数。
解题思路:
一眼 exgcd,但是场上不会,输。
推一下 exgcd 吧,首先要用到裴蜀定理,如果 \(ax+by=c\) 有解,则 \(c|gcd(a,b)\),那么我们就可以轻松判断有无解了。
思考如何搞出一组解,因为 \(gcd(a,b)=gcd(b,a\%b)\), 所以要求 \(ax+by=gcd(a,b)\) 的一组解,可以先求 \(bx+(a\%b)y=gcd(b,a \%b)\) 的一组解,然后往回带。
一直做到 \(b=0\),那么一组可行的解就是 \(x=1,y=0\)。
注意到 \(a \%b=a-\lfloor a/b\rfloor *b\),那么我们可以化式子: \(bx+(a-\lfloor a/b\rfloor *b)y \rightarrow ay+b(x-\lfloor a/b\rfloor y)\) 则新的 \(x,y\) 也随之而出了。
但是这样我们只是解出了一组解,怎么使得 \(x,y\) 的绝对值之和最小呢?
需要一个结论那就是 \(x\) 的最优解一定是取可能的最小正值或最大的非负值,比较即可,注意 \(0\) 的情况。
代码实现:
#include<bits/stdc++.h>
#define int long long
using namespace std;
void exgcd(int a, int b, int &x, int &y){
if(b == 0){
x = 1, y = 0;
return;
}
exgcd(b, a % b, x, y);
int t = x;
x = y, y = t - (a / b) * y;
return;
}
int gcd(int a, int b){
return b == 0 ? a : gcd(b, a % b);
}
int n, A, B, ans;
signed main(){
freopen("array.in", "r", stdin);
freopen("array.out", "w", stdout);
cin >> n >> A >> B;
int gcd1 = gcd(A, B);
A /= gcd1, B /= gcd1;
if(A > B) swap(A, B);
for(int i = 1; i <= n; i++){
int c;
cin >> c;
if(c % gcd1 != 0) return cout << "-1" << endl, 0;
c /= gcd1;
int x = 0, y = 1;
exgcd(A, B, x, y);
x = c * x, y = c * y;
int k = x / B, res = 0x3f3f3f3f3f3f3f3f;
for(int j = -2; j <= 2; j++)
res = min(res, abs(x - (k + j) * B) + abs(y + (k + j) * A));
ans += res;
}
cout << ans << endl;
return 0;
}
标签:return,数列,gcd1,int,exgcd,gcd
From: https://www.cnblogs.com/huangweiliang/p/18577429