将元素平分成差值最小的两个集合(DP)
现有若干物品,要分成较为平均的两部分,分的规则是这样的:
1)两部分物品的个数最多只能差一个。
2)每部分物品的权值总和必须要尽可能接近。
现在请你编写一个程序,给定现在有的物品的个数以及每个物品的权值,求出按上述规则分成两部分后每部分的权值总和。
输入格式
第一行为一个整数n(1≤n≤200),表示现在有的物品的个数。
以下的n行每行一个整数,表示每个物品的权值(1≤ai≤40)。
输出格式
只有一行,包含两个数,即分成的每部分的权值总和,较小的一个值放在前面,两个数用空格隔开。
样例输入
3 35 20 32
样例输出
35 52
对于本题,因为需要再计算过程中保证你计算的结果是n\2 或 n\2+1个物品。所以这个时候我们就必须要使用二维数组,来记录你使用物品个数。
最后我们在找出n\2 或 n\2+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\n",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\n",dp[n][0]); 57 else printf("Impossible\n"); 58 59 return 0; 60 }
-