本题是一个有趣的有后效性的题目,如果是考察n个数能否等于x大家肯定都会,背包嘛。
但是能否被x整除改怎么写呢?原来的状态不满足后无效性啊。考虑到x较小,最直接的方法就是增加一个维度。
维护一个flag[i][f]表示第i个数时能否组成%x==f,他的转移方程是
flag[0][0]=1;
flag[i][f]=flag[i-1][f-o[i]]||flag[i-1][f+o[i]];
但是f如果小于o[i]呢?如果f+o[i]大于x了呢?就很尴尬。
这个时候就要巧妙的运用模运算了。
其实我是从flag[i-1][f]出发,如果flag[i-1][f]==1,就可以更新加减o[i]的答案了。可以这样写。
if(flag[i-1][f]) flag[i][((f-t)%x+x)%x]=flag[i][(f+t)%x]=1;
减的时候先不管会不会小于0,就先减一下o[i],然后对x取余。如果是负数也不会小于-x,那就再加上一个x;然而如果减一下还是一个正数的话现在就会大于x,那就再模一次x呗。
加的时候就加一下后对x取余就好了。
复杂度是O(k*n*x)。
int i,f,t; int k,n,x; bool flag[21000][210]; int main() { ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); //freopen("123.in","r",stdin); //freopen("123.out","w",stdout); cin>>k; for(;k>0;k--) { cin>>n>>x; memset(flag,0,sizeof(flag)); flag[0][0]=1; for(i=1;i<=n;i++) { cin>>t; for(f=0;f<x;f++) if(flag[i-1][f]) flag[i][((f-t)%x+x)%x]=flag[i][((f+t)%x+x)%x]=1; } if(flag[n][0])cout<<"Divisible"<<endl; else cout<<"Not divisible"<<endl; } }