将元素平分成差值最小的两个集合(DP)

现有若干物品,要分成较为平均的两部分,分的规则是这样的:

1)两部分物品的个数最多只能差一个。

2)每部分物品的权值总和必须要尽可能接近。

现在请你编写一个程序,给定现在有的物品的个数以及每个物品的权值,求出按上述规则分成两部分后每部分的权值总和。

输入格式

第一行为一个整数n(1≤n≤200),表示现在有的物品的个数。

以下的n行每行一个整数,表示每个物品的权值(1≤ai≤40)。

输出格式

只有一行,包含两个数,即分成的每部分的权值总和,较小的一个值放在前面,两个数用空格隔开。

样例输入

3
35
20
32

样例输出

35 52

对于本题,因为需要再计算过程中保证你计算的结果是n2 或 n2+1个物品。所以这个时候我们就必须要使用二维数组,来记录你使用物品个数。

最后我们在找出n2 或 n2+1(n是奇数)个物品中权值最大的就行了。

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <iostream>
 4 #include <string>
 5 #include <math.h>
 6 #include <algorithm>
 7 #include <vector>
 8 #include <stack>
 9 #include <queue>
10 #include <set>
11 #include <map>
12 #include <sstream>
13 #include <ctime>
14 const int INF=0x3f3f3f3f;
15 typedef long long LL;
16 const int mod=1e9+9;
17 const LL MOD=1e9+7;
18 const double PI = acos(-1);
19 const double eps =1e-8;
20 #define Bug cout<<"---------------------"<<endl
21 const int maxn=1e5+10;
22 using namespace std;
23 
24 int a[205];
25 int dp[205][205*20];//dp[i][j]表示当前有i个物品总权值为j 
26 
27 int main()
28 {
29     #ifdef DEBUG
30     freopen("sample.txt","r",stdin);
31     #endif
32 //    ios_base::sync_with_stdio(false);
33 //    cin.tie(NULL);
34     
35     int n;
36     scanf("%d",&n);
37     int sum=0;
38     for(int i=1;i<=n;i++)
39     {
40         scanf("%d",&a[i]);
41         sum+=a[i];
42     }
43     dp[0][0]=1;
44     for(int i=1;i<=n;i++)
45     {
46         for(int j=n/2+1;j>=0;j--)
47         {
48             for(int k=sum/2;k>=0;k--)
49             {
50                 if(dp[j][k]) dp[j+1][k+a[i]]=1;
51             }
52         }
53     }
54     for(int i=sum/2;i>=0;i--)
55     {
56         if(dp[n/2][i]||(dp[n/2+1][i]&&n&1))
57         {
58             printf("%d %d
",i,sum-i);
59             break;
60         }
61     }
62     
63     return 0;
64 }

现有N个物品,每块物品有一个权值, 现在想将这N个物品分成权值相同的两部分,可以从这N个物品中任取M(1≤M≤N)个来构建。但是不知道这些物品能否使分成的两部分具有同样的权值,也不

 知道如果能分成权值相同的两部分,每部分的最大权值是多少。

给定物品的数量N(1≤N≤100)和每个物品的权值wi ( N个物品的权值总和不超过2000 )。

你的任务是判断能否在这些物品中挑一部分,把他们分成权值相同的两部分,如果能,则输出所能分成两部分的最大权值和,否则输出" Impossible"。

输入格式

输入的第一行为一个数 N,表示物品的数量。

第二行为 N 个数,第 i 个数表示第 i 个物品的权值。

输出格式

输出仅包含一行,如果能分成权值相同的两部分,则输出每部分的最大权值和,否则输出一个字符串"Impossible"。

样例输入

5
1 3 4 5 2

样例输出

7

本题用dp[i][j]表示已处理i件物品,而分成的两部分权值差为j的一种状态。

所以我们对这两部分我们有三种操作

1)把第i个物品放入总权值大的一部分中,大的变得更大

2)把第i个物品放入总权值小的一部分中,然后就会发生两种情况

(1)小的变成大的

(2)小的还是小的

 所以我们有三种状态转移方程。对应的代入如上。

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <iostream>
 4 #include <string>
 5 #include <math.h>
 6 #include <algorithm>
 7 #include <vector>
 8 #include <stack>
 9 #include <queue>
10 #include <set>
11 #include <map>
12 #include <sstream>
13 #include <ctime>
14 const int INF=0x3f3f3f3f;
15 typedef long long LL;
16 const int mod=1e9+7;
17 const double PI = acos(-1);
18 const double eps =1e-8;
19 #define Bug cout<<"---------------------"<<endl
20 const int maxn=1e5+10;
21 using namespace std;
22 
23 int a[105];
24 int dp[105][2005];
25 
26 int main()
27 {
28     #ifdef DEBUG
29     freopen("sample.txt","r",stdin);
30     #endif
31 //    ios_base::sync_with_stdio(false);
32 //    cin.tie(NULL);
33     
34     int n;
35     scanf("%d",&n);
36     int sum=0;
37     for(int i=1;i<=n;i++)
38         scanf("%d",&a[i]);
39     memset(dp,-1,sizeof(dp));
40     dp[0][0]=0;
41     for(int i=1;i<=n;i++)
42     {
43         sum+=a[i];
44         for(int j=0;j<=sum;j++)
45         {
46             if(dp[i-1][j] > dp[i][j])//不选(初始化)
47                 dp[i][j] = dp[i-1][j];
48             if(j >= a[i] && dp[i-1][j-a[i]] >= 0)//大 -> 大 
49                 dp[i][j] = max(dp[i][j],dp[i-1][j-a[i]]);
50             if(dp[i-1][j+a[i]] >= 0)//小 -> 小 
51                 dp[i][j] = max(dp[i][j],dp[i-1][j+a[i]]+a[i]);
52             if(a[i] >= j && dp[i-1][a[i]-j] >= 0)// 小 -> 大 
53                 dp[i][j] = max(dp[i][j],dp[i-1][a[i]-j]+a[i]-j);
54         }
55     }
56     if(dp[n][0]) printf("%d
",dp[n][0]);
57     else printf("Impossible
");
58     
59     return 0;
60 }

-

原文地址:https://www.cnblogs.com/jiamian/p/12219569.html