cf#319-div2-B. Modulo Sum-(dp) 求模

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


你可能感兴趣的:(cf#319-div2-B. Modulo Sum-(dp) 求模)