题意要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;
}