【codevs1959】拔河比赛

题目大意:给定一个有 N 个数的集合,将这 N 个数均分成两堆,求差值最小是多少。

题解:有关集合选数的问题,应该是背包问题,同时要求均分可知,选出的物品数目也应该是背包费用的一个维度,因此这是一个多维费用背包问题。设状态 \(dp[i][j][k]\) 表示考虑了前 i 个数字,已经选了 j 个数字,数字之和为 k 的情况是否可行,可知状态转移方程为 \(dp[i][j][k]|=dp[i-1][j-1][k-w[i]]|dp[i-1][j][k]\)。最后进行滚动数组优化即可。

代码如下

#include <bits/stdc++.h>
using namespace std;

int n,w[110],sum,dp[60][45000];

void read_and_parse(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&w[i]),sum+=w[i];
	dp[0][0]=1;
}

void solve(){
	for(int i=1;i<=n;i++)
		for(int j=n>>1;j>=1;j--)
			for(int k=sum;k>=w[i];k--)
				dp[j][k]|=dp[j-1][k-w[i]];
	int ans=0,tmp=sum;
	for(int k=1;k<=sum;k++)if(dp[n>>1][k]){
		if(abs(sum-2*k)<tmp)ans=k,tmp=abs(sum-2*k);
                else break;
	}
	printf("%d %d\n",min(sum-ans,ans),max(sum-ans,ans));
}

int main(){
	read_and_parse();
	solve();
	return 0;
}
posted @ 2018-11-27 20:30  shellpicker  阅读(686)  评论(0编辑  收藏  举报