转化题意:

给你n个数,问你是否能选出若干个数使得数字的和为3600的倍数(至少选一个)

一开始,我打的01背包,dp[i]表示和模3600为i的方案数

一开始,dp[0]为1,不难发现,若dp完后,dp[0]>1的话,就表示存在和为3600,不过,我们计算下复杂度:

很明显,对于此题是过不了的,所以我们需要优化

注意到,我们仅需知道dp[0]是否大于1而并不需要具体的值,所以,我们可以在dp中途判断下,dp[0]是否大于1,若是的话就推出即可,这样对于答案是Yes的情况,可以起到很大的优化,那么,对于No呢?很明显,是没法进行优化的,那么,我们考虑下该怎么做。。。(当然,你写个这个此题就可以直接过了)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+1,mod=3600;
int dp[2][3600];
int a[N];
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            a[i]%=mod;
        }
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(int i=1;i<=n;++i){
            if(dp[0][0]>1){
                break;
            }
            for(int j=0;j<3600;++j){
                int now=((j-a[i])%mod+mod)%mod;
                dp[1][j]+=dp[0][now];
            }
            for(int j=0;j<=3600;++j){
                dp[0][j]+=dp[1][j];
                dp[1][j]=0;
            }
        }
        if(dp[0][0]>1){
            puts("YES");
        }else{
            puts("NO");
        }
    }
    return 0;
}

注意到,我们的dp[i]表示的是模3600为i的方案数,那么,我们其实对于所有a[i],我们可以将它们根据模3600的结果分成3600组,现在,我们题目就转化为了,从3600组中,每组取若干个数,使得和模3600为0,而由于每组内的数值相同,我们相当于,题目转化为,有个1,个2,个3...个3599,选出模为0,(要是>0的话,直接输出YES就行了。。。)这个很明显,做一个二进制拆分即可~,然后对于复杂度中的"n"减到很小的程度,然后亲测,跑 @SemonChan 大佬给的hack数据,用时300ms ~

当然,我们还可以发现,二进制拆分后,我们又相当于是做同一道题,只是n值变小了,那么,我们还可以重复几次二进制拆分,23333

代码:(一次二进制拆分)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+1,mod=3600;
int dp[2][3600],tot[mod];
int a[N];
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        memset(tot,0,sizeof(tot));
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            a[i]%=mod;
            ++tot[a[i]];
        }
        if(tot[0]){
            puts("YES");
            continue;
        }
        n=0;
        for(int i=1;i<3600;++i){
            int now=1;
            while(tot[i]>=now){
                a[++n]=(now*i)%mod;
                tot[i]-=now;
                now<<=1;
            }
            if(tot[i]){
                a[++n]=(tot[i]*i)%mod;
            }
        }
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(int i=1;i<=n;++i){
            if(dp[0][0]>1){
                break;
            }
            for(int j=0;j<3600;++j){
                int now=((j-a[i])%mod+mod)%mod;
                dp[1][j]+=dp[0][now];
            }
            for(int j=0;j<=3600;++j){
                dp[0][j]+=dp[1][j];
                dp[1][j]=0;
            }
        }
        if(dp[0][0]>1){
            puts("YES");
        }else{
            puts("NO");
        }
    }
    return 0;
}