Jury Compromise POJ - 1015 双塔DP 输出路径
题意
n个陪审团的候选人,从这n个人中选m人组成陪审团。
选出的m个人,必须满足辩方总分和控方总分的差的绝对值最小。如果有多种选择方案的辩方总分和控方总分的之差的绝对值相同,那么选辩控双方总分之和最大的方案即可。
思路
DP过程
dp状态:
用三维DP dp[i][j][k]表示第i个人已经选择了j个人差值为k的总分和
转移:
选择当前点,不选择当前点求max。
处理偏移量
因为k这维度会出现负数;又因为最小为-400,最大为400,
所以我们需要将范围设为
0-800 。然后我们从中值出发,往左右移动,得到最小的体积。
理解是这样理解,反映到代码上,是将 dp[0][0][400]=0;
其余设为负无穷。
输出路径
复原路径可以从dp[n][m][v]出发(dp[n][m][v]是我们找到的答案)逆推到dp[x][0][y] (x,y未知),根据dp值之间的关系,把路径给复原出来。
具体看代码吧。
代码
#include <vector>
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <stack>
#define endl '\n'
// #define int long long
using namespace std;
const int N = 205, M = 805;
int n, m;
int a[N], b[N];
int dp[N][21][M];
void solve()
{
int kcase = 0;
while (cin >> n >> m && n && m)
{
cout << "Jury #" << ++kcase << endl;
for (int i = 1; i <= n; i++)
cin >> a[i] >> b[i];
memset(dp, -0x3f, sizeof dp);
dp[0][0][400] = 0;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= m; j++)
for (int k = 0; k < M; k++)
{
dp[i][j][k] = dp[i - 1][j][k];
int t = k - (a[i] - b[i]);
if (t < 0 || t >= M) continue;
if (j < 1) continue;
dp[i][j][k] = max(dp[i][j][k], dp[i - 1][j - 1][t] + a[i] + b[i]);
}
int v = 0;
//有一个大于0就停止
while (dp[n][m][400 - v] < 0 && dp[n][m][400 + v] < 0)
v++;
if (dp[n][m][400 - v] > dp[n][m][400 + v])
v = 400 - v;
else
v = 400 + v;
int asum = 0, bsum = 0;
int i = n, j = m;
stack<int> ans;
while (j)
{
if (dp[i][j][v] == dp[i - 1][j][v])
i--;
else
{
asum += a[i], bsum += b[i];
ans.push(i);
v -= (a[i] - b[i]);
i--, j--;
}
}
printf("Best jury has value %d for prosecution and value %d for defence:\n", asum, bsum);
while (ans.size())
{
int k = ans.top();
ans.pop();
cout << k << " ";
}
printf("\n\n");
}
}
signed main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
int t = 1;
// cin >> t;
while (t--)
solve();
return 0;
}