Time Limit: 1 second
Memory Limit: 128 MB
【问题描述】
对于任意一个整数数列,我们可以在每两个整数中间任意放一个符号'+'或'-',这样就可以构成一个表达式,也就可以计算出表达式的值。比如,现在有一个整数数列:17,5,-2,-15,那么就可以构造出8个表达式: 17+5+(-21)+15=16 17+5+(-21)-15=-14 17+5-(-21)+15=58 17+5-(-21)-15=28 17-5+(-21)+15=6 17-5+(-21)-15=-24 17-5-(-21)+15=48 17-5-(-21)-15=18 对于一个整数数列来说,我们能通过如上的方法构造出不同的表达式,从而得到不同的数值,如果该数值能够被k整除的话,那么我们就称该数列能被k整除。 在上面的例子中,该数列能被7整除(17+5+(-21)-15=--14),但不能被5整除。现在你的任务是,判断某个数列是否能被某数整除。【输入格式】
第一行是一个整数m,表示有m个子任务。接下来就是m个子任务的描述。 每个子任务有两行。第一行是两个整数n和k(1<=n<=10000, 2<=k<=100),n和k中间有一个空格。n 表示数列中整数的个数;k就是需要你判断的这个数列是否能被k 整除。第二行是数列的n个整数,整数间用空格隔开,每个数的绝对值都不超过10000。
【输出格式】
输出文件应有m 行,依次对应输入文件中的m 个子任务,若数列能被k 整除则输出 "Divisible",否则输出 "Not divisible" ,行首行末应没有空格。
【数据规模】
Sample Input1
2 4 7 17 5 -21 15 4 5 17 5 -21 15
Sample Output1
Divisible Not divisible
【题解】
这是一道动态规划的问题。
设f[i][j]表示前i个数余数为j的情况是否出现
则
f[i+1][(j+a[i+1])%k] = f[i+1][(j+a[i+1])%k] || f[i][j];
f[i+1][|j-a[i+1]|%k] = f[i+1][|j-a[i+1]|%k] || f[i][j];
然后一开始,如果输入的n个数字,出现负数,就直接取相反数改为正数就好。
这里用到了同余率(反正就是(a+b-c) % k == ((a+b) %k - c)%k 这样。因为我的题A掉了。所以这个等式应该是成立的。。
然后对于每一个数字,只有加或减两张情况。
然后之所以用余数来表示,是因为余数这个变量的k值不大,最大只有100.是适合用来作为状态的。
如果f[n][0]为true,则表示这个数列能够被整除。
【代码】
#include <cstdio> #include <cstring> bool f[10001][101]; int a[10001]; int m, n,k; int main() { scanf("%d", &m); //有m组数据 for (int i = 1; i <= m; i++) { memset(f, false, sizeof(f)); scanf("%d%d", &n, &k); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); //如果是负数就直接改为正数就好。 if (a[i] < 0) a[i] = -a[i]; } f[1][a[1] % k] = true;//第一个数前的符号是不能改的。 for (int i = 2;i <= n;i++) for (int j = 0;j <= k;j++)//其他数字的符号则可以改。 if (f[i - 1][j]) { int temp = j + a[i];//加法的话temp不会出现负数所以不用加绝对值。 temp = (temp % k); f[i][temp] = true; temp = j - a[i];//减法就可能为负数了。要注意。 if (temp < 0) temp = -temp; temp = (temp % k); f[i][temp] = true;//从前一个状态推到当前的状态。 } if (f[n][0])//如果前n个数的余数为0.就表示这个数列能够被整除。 printf("Divisible "); else printf("Not divisible "); } return 0; }