【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;
}

猜你喜欢

转载自www.cnblogs.com/wzj-xhjbk/p/10028829.html