本题实际上问的是:
对于方程ax + by + cz + ...... = Const方程而言(a,b,c...是系数,x,y,z...是变量,Const是常数),对于任意给定的Const,是否存在整数x,y,z...,使得方程成立。
实际上,对于这类整数不定方程,我们有以下性质:
设a,b,c...的最大公因数为t:
1.若t = 1(即这几个数互质),那么方程一定有解,且使得成立的x,y,z...有无数多个。其中,x,y,z...的解空间是整数。
2.若t = 1,x,y,z...的解空间是非负数时,方程可能无解,但是,使方程无解的Const有限,这种Const不超过max(a,b,c...)*max(a,b,c...).
3.若t > 1,则有无数多个Const,使得方程无解。
【注】n个数互质不是两两互质,而是t == 1.
首先先找最大公因数:
int gcd(int a,int b){//get the gcd of the num a & b
return b==0?a:gcd(b,a%b);
}
这是一种递归写法
本题思路:背包/状态转移
一、(完全)背包做法
本题可以通过完全背包求解的原因是,题目中说“每种蒸笼都有非常多笼,可以认为是无限笼”。那么可以用完全背包实现这个题目。设一位数组f[k] (k在0到10000)。但这个f[k]不牵扯到价值问题,表示的是 -- k这个数能否由已知的数字表示?1:0.
那么设置f[0] = 1,因为0一定能被表示出来;其它default为0.
对于获得最大价值的完全背包问题,代码是: dp[ j ] += max(dp[ j-w[i] ],dp[ j ]);
这表征的是对每个j获得最大价值,最后dp[v]就是最大价值数。
而本题不是考虑“价值”,而是能否满足,那么仅需写作:
dp[ j ] = max(dp[ j-w[i] ],dp[ j ]);//+= 换成 = ,因为如果是1,取完max之后就可以一直保持,表征本j可以被表示。
实际上,为了减小运算,还可以写成这样:
for(int i = 1;i <= n;i++){
for(int j = w[i];j <= 10000;j++){//注意j从小到大
f[j] = max(f[j] , f[j-w[i]]);//这是存在性问题,找到一种可达方案即可。
}
}
//注意:这里的max实际上是取0和1中的1,一旦j可以被表示出来,那么它f[j]就是1,不再改变。
这样,最终未被标记的(f[ k ] == 0)点,就是无法被表示出的点。
二、状态转移法
这本质上和背包是一样的,大概的思路还是设置f[ 0 ] = 1,因为0可以被表示。
然后:
for(int i = 1;i <= n;i++){
for(int j = 0;j <= 10000;j++){//注意j从0开始
if(j+w[i] > 10000) continue;//越界特判
if(f[j]) // 如果j可以被凑出
f[j+w[i]] = 1;//类似递归,只不过是“正向思路”
}
}
这样最后没有被标记的数也是无法被表示出来的数。
三、AC代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int a[10005] = {0};
int w[105] = {0};
int n;
int gcd(int a,int b){
return b == 0?a:gcd(b,a%b);
}
int max(int a,int b){
return a>b?a:b;
}
int main(){
int gcdnum = 1;
cin >> n;
for(int i = 1;i <= n;i++){
cin >> w[i];
}
gcdnum = w[1];
for(int i = 2;i <= n;i++){
gcdnum = gcd(gcdnum,w[i]);
}
if(gcdnum != 1){
printf("INF\n");
return 0;
}
a[0] = 1;
int ans = 0;
// for(int i = 1;i <= n;i++){
// for(int j = w[i];j <= 10000;j++){
// a[j] = max(a[j],a[j-w[i]]);
// }
// }
for(int i = 1;i <= n;i++){
for(int j = 0;j <= 10000;j++){
if(j+w[i] > 10000) continue;//越界特判
if(a[j]) // 如果j可以被凑出
a[j+w[i]] = 1;//类似递归,只不过是“正向思路”
}
}
for(int i = 1;i <= 10000;i++){
if(a[i] == 0) ans++;
}
printf("%d\n",ans);
return 0;
}