背包DPJury Compromise POJ - 1015
Posted Vincent_0000
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了背包DPJury Compromise POJ - 1015相关的知识,希望对你有一定的参考价值。
题目提交点
题目分析
- 题意分析
从n个人中选m个人做为陪审团成员,有n个人的辩方分和控方分,求选出的m个人中是所有情况中总分差是最小,总分差一样是求总分之和最大的情况。
- 题型分析
从n个人中选m个人,有点类似于01背包问题,从背包问题的角度考虑这个题目。
- DP分析
我们的DP集合要能够代表题目的所有情况,不一定我们的集合就一定能够一步到位算到我们要的答案上去,但是我们可以通过间接的方式得到我们需要的答案。
我们的集合表示:
f
(
i
,
j
,
k
)
f(i, j, k)
f(i,j,k)表示从前i个人中选j个人,总方差为k的总分之和的集合。
属性为:max
这样的集合表示就将我们题目所有的情况表达出来了,但是没有直接的得到我们想要的答案,我们可以通过其他的方式得出我们的答案。
状态划分:(从前一个状态转移的情况进行考虑)
f
(
i
,
j
,
k
)
=
m
a
x
(
f
(
i
−
1
,
j
,
k
)
,
f
(
i
−
1
,
j
−
1
,
k
−
v
i
)
+
w
i
)
f(i, j, k) = max(f(i - 1, j , k ), f(i - 1, j - 1, k - v_i) + w_i)
f(i,j,k)=max(f(i−1,j,k),f(i−1,j−1,k−vi)+wi)
v
i
v_i
vi代表第i个人的辩方和控方总分差,
w
i
w_i
wi代表第i个人的辩方和控方总分和。
接下来就是从我们表示的集合中提取我们想要的信息,题目需要m个人的辩方总分和控方总分,以及m个人的编号。
分数差有正有负的情况,
−
20
≤
v
i
≤
20
-20 \\leq v_i \\leq 20
−20≤vi≤20, 我们m有个陪审团成员而且数组下标不能为负数,所以
0
≤
k
≤
40
∗
m
0 \\leq k \\leq 40*m
0≤k≤40∗m,那么就代表20 * m为零点,在左边为负数,在右边为正数。
我们从零点开始往两边找到到第一个存在总分数之和的地方进行处理, 如果找到的值在左边,那么说明辩方的分数较少,总分减去差值/2即可得到答案,其他情况同理。
最后一步就是记录路径的问题了,使用vector将每一种情况记录下来。
AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define ENDL "\\n"
typedef vector<int> vi;
const int N = 200 + 7, M = 800 + 7, INF = 0x3f3f3f3f;
int f[N][M], v[N], w[N];
vi path[N][M];
int main() {
#ifdef LOCAL
freopen("data.in", "r", stdin);
#endif
int n, m, T = 1;
while (cin >> n >> m && n && m) {
int zero = 20 * m;
memset(f, -0x3f, sizeof f);
_rep(i, 0, m) _rep(j, 0, zero * 2) path[i][j].clear();
f[0][zero] = 0;
_rep(i, 1, n) {
int a, b;
cin >> a >> b;
int v = a - b, w = a + b;
For(j, m, 1) _rep(k, v, 2 * zero)
if (k - v < M && f[j][k] < f[j - 1][k - v] + w) {
f[j][k] = f[j - 1][k - v] + w;
path[j][k] = path[j - 1][k - v];
path[j][k].push_back(i);
}
}
int x;
for (x = 0; f[m][zero + x] < 0 && f[m][zero - x] < 0; ++x);
int y = (f[m][zero + x] > f[m][zero - x])? x : -x;
int pro = (f[m][zero + y] + y) / 2;
int def = (f[m][zero + y] - y) / 2;
printf("Jury #%d\\nBest jury has value %d for prosecution and value %d for defence:\\n", T++, pro, def);
_for(i, 0, m) cout << " " << path[m][zero + y][i];
cout << ENDL << ENDL;
}
return 0;
}
反思
对于这道题目,我没有将DP集合分析到位,没有将几个限制条件加到一个集合中,导致后面的数据出现很多的冲突。
要记住,我们分析DP集合,分析的是大方向是能够将题目所有的集合包含进去的集合,答案只是我们分析集合顺带的一个解,如果没有直接求出我们想要的答案,我们就需要从我们所设的集合中提取我们想要的答案。
我写的代码没有严格的选取m个人,对于这个知识点之前没有弄懂,经过这道题目之后,对于终于弄懂了。
我选取m个人的时候,没有一个从m -1 个人到m个的过程,也就是说没有将m这个变量放到我们状态划分中去。
而对于我们平常所学的选取m个人,那是严格的选取了m个人,因为它有一个从m - 1到m 的一个转移过程,对于动态规划,变量的转移是非常重要的!!
以上是关于背包DPJury Compromise POJ - 1015的主要内容,如果未能解决你的问题,请参考以下文章
POJ 1015 / UVA 323 Jury Compromise(01背包,打印路径)
POJ 1015 Jury Compromise 2个月后重做,其实这是背包题目