这道题感觉是枚举加一些优化在里面。
还是以例子为例吧。dp[i]=1表示某种组合加起来除以3600的余数为i,一开始的时候dp[]数组全为0;当某个组合是3600的倍数的时候,dp[0]=1,这便是说明可以组合形成3600的倍数,便可以输出YES,否则输出NO。
假如:
2000 1000 3000
一开始2000加入的时候,2000%3600=2000,便标记dp[2000]为1
下次加入1000的时候遍历dp数组一遍,会发现dp[1600]为1,便在这个基础上加上a[i]再对3600取模(这里因为只要1600被标记了故v中只加入了(1000+1600)%3600=1000),结果存储在v数组中,最后再用将v数组中存储的放入dp数组中(dp[1000]=1),在标记(1000%3600=2600)dp[2600]=1;
这样我们可以发现会大大缩小枚举范围,比如很多组合加起来%3600都为4,在下一次加入时候,我们只要计算一次便可以得出结果是多少。
#include<bits/stdc++.h> using namespace std; int T,m,a[100005];//用于存储初始数据 int dp[3605]; int v[10000];暂存下余数 int main() { scanf("%d",&T); while(T--) { memset(dp,0,sizeof(dp)); scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d",&a[i]); } for(int i=1;i<=m&&!dp[0];i++) { int cnt=0; for(int j=1;j<3600;j++) { if(dp[j])//如果发现了之前计算保留的值 { v[cnt++]=(j+a[i])%3600; } } for(int j=0;j<cnt;j++) { dp[v[j]]=1; } dp[a[i]%3600]=1; } if(dp[0]) printf("YES\n"); else printf("NO\n"); } }