题意要n个数中 看能否选出一个组合,使其之和为 m的倍数
开一个dp[1001]、tmp_dp[1001]
dp[i]的意思,是能得到 一个组合,其和为sum 且sum%m==i 那么我们就让dp[i]=1;表示存在
在递推的过程中,如果我们在前i个数得到dp[j]=1;也就是存在sum1%m==j;
那么对于a[i+1]、我们可以推得 一个sum2=sum1+a[i+1]、所以得到sum2%m==k; 也就是dp[k]=1;
即: 由在前i次循环中,如果得到dp[j]=1;可以在第i+1次循环得到 dp[ ( j+a[i+1] )%m ]=1;
这里为什么强调前i次循环和第i+1次循环? 因为要得到dp[k]=1,其条件中的dp[j]=1;必须在 a[i+1]的递推之前就已经得到
这里给个反例, a[1]=1,m=100如果我从a[1]=1;推得dp[1%100]=dp[1]=1; 然后我又根据这个dp[1]=1得到dp[(1+a[1])%100]=dp[2]=1;这样就一直推下去了
所以我们在从dp[j]推到dp[k]的时候,dp[k]应该记录在一个tmp_dp[k]数组里(为了不在递归中影响dp数组的值)
做完整个递推后,才把tmp_dp[]的值赋给dp;
还有一点是dp[0]就意味有 sum%m==0,也就是答案找到了,此时直接break,否则超时;
#include <cstdio> #include <cmath> #include <cstring> #include <string> #include <algorithm> #include <iostream> #include <queue> #include <list> #include <set> #include <vector> using namespace std; int dp[1005]; int tm[1000005]; int tmp_dp[1005];//主函数中解释; int main() { int n,m; int i,j; scanf("%d%d",&n,&m); for (i=1;i<=n;i++) scanf("%d",&tm[i]); for (i=1;i<=n;i++) { if (dp[0]) break; for (j=1;j<m;j++) { if (dp[j])//此处dp[j]为上次循环所留下的结果 { tmp_dp[(j+tm[i])%m]=1;//如果直接改dp数组的值,上面的判断会出问题,会出现利用"自己得到自己的两倍"这样的情况 }//此处意思是,如果dp[j]存在,即能组成 一个sum,使得sum%m=j,那么sum+tm[i]得到sum2,所以sum2%m也是可以得到的*最终目标是求sumX%m==0 // 也就是有整除m的数存在就可以停止循环了,算是有点贪心吧。 } tmp_dp[tm[i]%m]=1; for (j=0;j<m;j++) dp[j]=tmp_dp[j]; } if (dp[0]) printf("YES\n"); else printf("NO\n"); return 0; }