关于北大acm1745问题分析讨论
以下依次给出题目、我的分析、代码 ,我是第一次作acm,总是通不过,请大家帮忙参考一下。
http://acm.pku.cn/JudgeOnline/showproblem?problem_id=1745
中文大意(好像我没理解错吧?)
对N(1<=N<=10000)个数字,在数字之间添加加号或者减号,每个组合可以计算出一结果。判断这些结果中是否存在能够被K(2<=K<=100)除尽的数字。
比如对:17, 5, -21, 15这4个数字,添加加号或者减号可以得到:
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为7,那么可以说:存在17 + 5 + -21 - 15 = -14 能被7除尽。
输入格式:
数据第一行有两个整数,N(1<=N<=10000),K(2<=K<=100)。数字之间由空格分开。第二行是N个数字,他们的绝对值不超过10000,数字之间由空格分开。
输出格式:
如果存在一个组合能被K除尽,那么输出"Divisible",否则输出"Not divisible"。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
分析:
一、此题考察结果是否整除,即表示为是否有余数;
二、N个数相加减,所的结果除以K的余数,等于它们各自对K的余数相加减;
三、这些余数计算的结果可能会有重复的,并且只落在[0,K)范围内;
四、因此,不必列出N个数相加减的所有组合,只需从头开始逐步计算结果(并取余),每一步的结果再加减下一个,直到最后产生最终不超过K个的结果;
五、上述表达用数学描述为:设 计算到第n个的结果为集合A,下一个余数为b,则第n+1个结果为{t+b,t-b|t属于A};
六、考察最后的结果集合中是否有0,即表示有无整除项。
程序采用C语言,数组方案,用大小为K的数组表示某一步的结果集合,元素用1,0表示有无相应的余数项,如:某一步有结果为i,则令p[i]=1;
为方便计算设两个数组,p1是上一步的结果集合,运算时先把p2清零,把p1中元素为1的项数逐个拿来计算,结果映射到p2中去,完成后交换两个数组,进入下一步;
读数的时候第一个数据单独处理(因为没有0+t,0-t),后面的数据用循环;
最后判断p1[0]是否为1即可。
~~~~~~~~~~~~~~~~~~~~~~~~
代码:
#include<stdio.h>
int main()
{
int n,k,i,d,c,t;
char *p1,*p2,*pt;
char a[100],b[100];
scanf("%d%d", &n, &k);
p1=a;
p2=b;
scanf("%d",&d);
d%=k;
if(d<0)d+=k;
for(i=0;i<k;i++)
p1[i]=0;
p1[d]=1;
c=0;
while(--n)
{
scanf("%d",&d);
if(c)continue;
d%=k;
if(!d)continue;
if(d<0)d+=k;
for(i=0;i<k;i++)
p2[i]=0;
c=0;
for(i=0;i<k;i++)
{
if(p1[i])
{
c++;
t=i+d;
if(t>=k)t-=k;
p2[t]=1;
t=i-d;
if(t<0)t+=k;
p2[t]=1;
}
}
if(c!=k)
{
c=0;
pt=p1;
p1=p2;
p2=pt;
}
}
if(*p1||c)
puts("Divisible");
else
puts("Not divislble");
return 0;
}