【DP】:CF #319 (Div. 2) B. Modulo Sum

【题目链接】:http://codeforces.com/contest/577/problem/B

【相似题目】:http://swjtuoj.cn/problem/2383/

 

【题意】:给出n个数,问是否能从中选出一些数,使得这些数的和是m的倍数。

 

【题解】:

首先,先明白这样一个事实:

设:sum%dend=rem;

(sum:一些数的和,dend:被除数,rem:余数)

则有:(rem+n)%dend=(sum+n)%dend;

(n为一个新的数)

 

知道了上面的等式之后,题目就好做了:

设:est[rem]=1 表示存在余数rem,它是通过一些数的和sum%dend得到的。

对于每一个给定的n,

考察 1<=rem<=dend-1 范围内的est[rem],即考察是否存在之前一些数的和sum%dend=rem(不管sum是之前的数是怎么相加得来的)

若存在,即est[rem]=1,则可以通过(rem+n)%dend来求出新的余数,即令est[(rem+n)%dend]=1;

此外,n%dend也是新的余数,即est[n%dend]=1;

若在上面的余数中有一个等于0,即est[0]=1,则说明存在dend的倍数,直接中断循环。

 

【注意】

在对每一个n的循环中,不能立刻更新est数组,原因是某些情况会导致出错。

出错例子:

设:已有est[1]=1,此时n=1,dend=100;

若令est[(1+n)%dend]=1,即est[2]=1,

则又有est[(2+n)%dend]=1,即est[3]=1,

又有est[(3+n)%dend]=1,即est[4]=1......

最后整个est数组都为1,显然这是错误的。

所以,要一个 i_est 数组来临时更新est数组,最后再更新est数组(详见代码)。

 

 

 1 #include<stdio.h>
 2 int num,dend,t,i,n[1000050];
 3 char est[1050],i_est[1050];
 4 int main(){
 5     scanf("%d%d",&num,&dend);
 6     for(t=0;t<num;t++){
 7         scanf("%d",&n[t]);
 8     }
 9     for(t=0;t<num;t++){
10         for(i=1;i<dend;i++){
11             if(!est[i]) continue;
12             i_est[(i+n[t])%dend]=1;
13         }
14         i_est[n[t]%dend]=1;
15         for(i=0;i<dend;i++){
16             est[i]=i_est[i];
17         }
18         if(est[0]) break;
19     }
20     if(est[0]){
21         printf("YES\n");
22     }
23     else{
24         printf("NO\n");
25     }
26     return 0;
27 }
View Code

 

posted @ 2017-05-14 15:56  Ctfes  阅读(264)  评论(0编辑  收藏  举报