UVa:10616 Divisible Group Sums

怎么看都感觉像个计数类的DP,但是数太大用01背包计数明显存不下,还好是问整除,所以取余即可,这样每个数最大可以不超过20。因为有负数所以得用带余除法来取余。

dp方程是dp[i][j][k]=dp[i-1][j-1][k-v[i]]+dp[i-1][j][k]

dp[i][j][k]表示前i个数使用j个组成和为k时的组合种数。用了滚动数组优化了第一维。

有个特别注意的地方就是k的范围,我一开始将k的范围设为sum(1……i)即v[1]到v[i]的和,但是WA了,后来改为v[1]到v[n]的和才AC的。。

另外就是数可能比较大,要用long long。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<algorithm>
#define MAXN 100005
#define INF 10000000
#define ll long long
//ios::sync_with_stdio(false);
using namespace std;
ll dp[3][15][4005];
int Mod(int val,int mod)
{
    if(val>=0) return val%mod;
    if(val%mod==0) return 0;
    int v=val/mod-1;
    return val-v*mod;
}
int main()
{
    int N,Q,kase=0;
    // freopen("my.txt","w",stdout);
    while(scanf("%d%d",&N,&Q)!=EOF)
    {
        if(!N&&!Q) break;
        int arr[205]= {0};
        for(int i=1; i<=N; ++i)
            scanf("%d",&arr[i]);
        printf("SET %d:\n",++kase);
        int D,M;
        for(int t=1; t<=Q; ++t)
        {
            int v[205]= {0};
            scanf("%d%d",&D,&M);
            printf("QUERY %d: ",t);
            int sum=0;
            memset(dp,0,sizeof(dp));
            for(int i=1; i<=N; ++i)
            {
                v[i]=Mod(arr[i],D);
                sum+=v[i];
            }
            dp[0][0][0]=1;
            dp[1][0][0]=1;
            for(int i=1; i<=N; ++i)
            {
                for(int j=1; j<=min(i,M); ++j)
                {

                    for(int k=0; k<=sum; ++k)
                    {
                        if(k<v[i])
                            dp[i&1][j][k]=dp[(i+1)&1][j][k];
                        else
                            dp[i&1][j][k]=dp[(i+1)&1][j-1][k-v[i]]+dp[(i+1)&1][j][k];
                    }

                }
            }
            ll ans=0;
            for(int i=0; i<=sum; i+=D)
                ans+=dp[N&1][M][i];
            printf("%lld\n",ans);
        }
    }
    return 0;
}


 

你可能感兴趣的:(数学,动态规划)