一 题意描述:
给你N(1<=N<=10,000)个数字,你可以在它们之间的N-1个位置任意放“+”或“-”,放完之后,我们可以计算出表达式的值。 存不存在一种摆放方案使得最后表达式的值可以被K(1<=K<=100)整除。
二 思路分析:
1.基础知识巩固之负数除以正数的余数:
由带余除法,任何一个整数n都可以表示成
n=k*q+r 其中0<=r<q
这里的r就是n除以q的余数
通常记为
n≡r(mod q)
例如
-9=(-2)*5+1
则-9除以5的余数为1。为了保证余数为正,那对于一个负数s(s的绝对值小于k),要求它除以k的余数,那么我们可以用(s+k)%k求得余数。
2.如何引入dp?
我们用f[i][j]=1表示前i个数处理后所得余数为j(0<=J<K),如f[i-1][j]=1;
那么对于第i个数(不妨设为正,任何情况下都取该数的绝对值)而言,有两种选择+或者-。
对于+:处理第i个数后的余数为(j+abs(a[i])%k)%k.那么f[i][(j+abs(a[i])%k)%k]=true;
对于—:处理第i个数后的余数为(j-abs(a[i])%k+k)%k.那么f[i][(j-abs(a[i])%k+k)%k]=true;
如此往复,直到处理完最后一个数。
下面开始判断:如果f[n-1][0]=true,说明这种情况可以被整除,输出visiable.否则输出disvisable。
三 源码:
1 #include <iostream> 2 #include <fstream> 3 #include <cstdio> 4 #include <cstring> 5 #include <cstdlib> 6 using namespace std; 7 const int N = 10000 + 10; 8 const int K = 100 + 10; 9 bool f[N][K]; 10 int a[N]; 11 int main() 12 { 13 int n,k; 14 int t; 15 cin>>t; 16 while(t--) 17 { 18 for(int i=0;i<n;i++) 19 cin>>a[i]; 20 memset(f,false,sizeof(f)); 21 f[0][abs(a[0])%k]=true; 22 for(int i=1;i<n;i++) 23 { 24 for(int j=0;j<k;j++) 25 if(f[i-1][j])//如果上一次余数存在且为j 26 { 27 f[i][(j+abs(a[i])%k)%k]=true;//(a+b)%k的余数等于( a%k +b%k )%k. 28 f[i][(j+k-abs(a[i])%k)%k]=true;//此处加k是为了避免余数为负值。 29 } 30 } 31 if(f[n-1][0]) cout<<"Divisible"<<endl; 32 else cout<<"Not divisible"<<endl; 33 } 34 return 0; 35 }