题意
给你四个序列,对于每一个序列:枚举序列中的值的选择情况使得已选择的部分和剩下的部分的差值最小。
二进制枚举
可以不写成dfs
#include<iostream>
using namespace std;
const int N = 30, INF = 0x3f3f3f3f;
int minv = INF;
int w[N];
int s[4];
void dfs(int u, int k, int sum, int len){
if(u > len){
minv = min(minv, max(k, sum - k));
return;
}
dfs(u + 1, k + w[u], sum, len);
dfs(u + 1, k, sum, len);
}
int main(){
for(int i = 0; i < 4; i ++) cin >> s[i];
int ans = 0;
for(int i = 0; i < 4; i ++){
int sum = 0;
for(int j = 1; j <= s[i]; j ++) cin >> w[j], sum += w[j];
minv = INF;
dfs(1, 0, sum, s[i]);
ans += minv;
}
cout << ans;
return 0;
}
dp
在分析出题意以后,计序列中元素总和为sum,序列长度为n,可以发现要使得从一个序列中枚举选择情况使得两部分的差最小,也就是说此种选择情况应该和sum / 2之差最小。
那么这个问题就转化为一个容量为sum / 2的背包,有n个物品,求能够将背包装到最满的的体积,这个问题是一个01背包问题,只不过物品价值和体积是相同的。
#include<iostream>
#include<cstring>
using namespace std;
const int N = 30, INF = 0x3f3f3f3f;
int w[N];
int s[4];
int f[610];
int dp(int n, int m){
memset(f, 0, sizeof f);
for(int i = 1; i <= n; i ++)
for(int j = m; j >= w[i]; j --)
f[j] = max(f[j], f[j - w[i]] + w[i]);
return f[m];
}
int main(){
for(int i = 0; i < 4; i ++) cin >> s[i];
int ans = 0;
for(int i = 0; i < 4; i ++){
int sum = 0;
for(int j = 1; j <= s[i]; j ++) cin >> w[j], sum += w[j];
ans += sum - dp(s[i], sum / 2);
}
cout << ans;
return 0;
}