多重部分和问题

有n种不同大小的数字ai,每种各mi个。判断是否可以从这些数字之中选出若干使它们的和恰好为K。

限制条件 1<=n<=100,1<=ai,mi<=100000,1<=K<=100000.

dp[i+1][j]:用前i种数字能否加和成K

为了前i种数字能加和成K,需要能用前i-1种数字加和成j,j-a...j-mi*ai中的某一种

因此dp[i+1][j]=(0<=k<=mi且k*ai<=j时存在使得dp[i][j-k*ai]为真的k)

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<memory.h>
 4 #define INF 10000000
 5 using namespace std;
 6 const int max_n=101;
 7 int a[max_n+1];
 8 int m[max_n+1];
 9 int n,r,K;
10 int main()
11 {
12     cin>>n;
13     for(int i=0;i<n;i++)
14     {
15         cin>>a[i]>>m[i];
16     }
17     cin>>K;
18     bool dp[n+1][K+1];
19     fill(dp[0],dp[0]+K+1,false);
20     dp[0][0]=true;
21     for(int i=0;i<n;i++)
22     {
23         for(int j=0;j<=K;j++)
24         {
25             for(int k=0;k*a[i]<=j&&k<=m[i];k++)
26             {
27                 dp[i+1][j]|=dp[i][j-k*a[i]];
28             }
29         }
30     }
31     cout<<dp[n][K]<<endl;
32     return 0;
33 }
View Code

优化如下

dp[i+1][j]:用前i种数加和得到j时第i种数最多能剩余多少个

dp[i+1][j]=mi  (dp[i][j]>=0)

      -1  (j<ai||dp[i+1][j-ai]<=0)

      dp[i+1][j-ai]-1  (其他)

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<memory.h>
 4 #define INF 10000000
 5 using namespace std;
 6 const int max_n=101;
 7 int a[max_n+1];
 8 int m[max_n+1];
 9 int n,r,K;
10 int main()
11 {
12     cin>>n;
13     for(int i=0;i<n;i++)
14     {
15         cin>>a[i]>>m[i];
16     }
17     cin>>K;
18     int dp[K+1];
19     memset(dp,-1,sizeof(dp));
20     dp[0]=0;
21     for(int i=0;i<n;i++)
22     {
23         for(int j=0;j<=K;j++)
24         {
25             if(dp[j]>=0)
26             {
27                 dp[j]=m[i];
28             }
29             else if(j<a[i]||dp[j-a[i]]<=0)
30             {
31                 dp[j]=-1;
32             }
33             else
34             {
35                 dp[j]=dp[j-a[i]]-1;
36             }
37         }
38     }
39     cout<<dp[K]<<endl;
40     return 0;
41 }
View Code
原文地址:https://www.cnblogs.com/wangkaipeng/p/6430144.html