题目描述:
给出n个整数,对每个整数取正或取负求和,可得2^n个数。问这些数当中是否存在一个数能被k整除。n<=10000,k<=100
分析:
由于n特别大,直接按照题意求出2^n个数再判断必然是不行的。
此题可考虑用动态规划。
令e[i][j]表示前i个数求和的得数能否模k得到j,i从1开始。动态转移方程e[i][j]=abs(p±a[i])%k,其中e[i-1][p]==1。若最后e[n][0]==1则有解,否则无解。
这里用到的原理是 (a+b)%m == (a%m+b%m)%m
===============================================================
/*
ZJU2042 Divisibility
*/
#include <stdio.h>
#include <string.h>
#define clr(a) memset(a,0,sizeof(a))
#define N 10001
#define M 101
int a[N];
int b[N][M],p,q;
int e[N][M];
int n,m;
int abs(int x){
return x>0?x:-x;
}
int main()
{
int i,j,k,T,flag;
scanf("%d",&T);
while(T--){
//input
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++) scanf("%d",&a[i]);
//init
clr(b); clr(e);
p=1;
b[0][0]=0;
e[0][0]=1;
//work
for(i=1;i<=n;i++){
q=0;
for(j=0;j<p;j++){
k=abs(b[i-1][j]+a[i])%m;
if(!e[i][k]){
e[i][k]=1;
b[i][q++]=k;
}
k=abs(b[i-1][j]-a[i])%m;
if(!e[i][k]){
e[i][k]=1;
b[i][q++]=k;
}
}
p=q;
}
//output
if(e[n][0]) puts("Divisible");
else puts("Not divisible");
if(T) puts("");
}
return 0;
}