您的位置:首页 > 其它

poj 1015 Jury Compromise

2012-07-30 02:25 204 查看
http://poj.org/problem?id=1015

  周日,结束了持续两个星期的个人赛,那是多么难以忘怀的一次选拔,成绩可谓差到谷底了...心情也是一样。晚上公布了个人赛的统计结果,庆幸的是训练依然继续,但是将会和谁组队,就只好看明天下午有哪位大牛会好心收留我了。恶心的结果,也是意料之内的...心情再怎么不好,还是应该为能够继续训练偷笑一下,缓解这一个星期的郁闷!

  在个人赛中,出现很多种类型的题目....什么网络流,二分匹配,凸包,线段树(我打算在未来几天里将我前几天弄懂的线段树写一下),生成树计数...甚至出现了随机算法 快速分解质因数 (链接里是一个比较简明的Pollard-rho算法代码)。

  

  晚上回到宿舍,在poj和hdu找了些dp的题目练了一下,这是其中一题。题意可以理解为,给出n种物品,求取其中m种,使得这m种物品的分别两种属性的和的差距(和的绝对值)最小。如果有多种情况,输出和最大的一种,并且要输出选择的物品的编号。

  一开始用物品的两种属性进行dp,用boolean数组来判断能达到的位置,可是这时的时间复杂度是O(n*m*maxsum^2),刚开始算错数了,没发觉这个问题..交上去果断返回一个TLE。于是,我构思了一下,更改了dp的状态。因为想到,直接记录他们的差和和就可以推出原来的两个数。两者的差不取绝对值,因此有可能是负数,所以要进行数组的平移,最后构造出类似天平的一个结构。

dp的几个状态:

第一个状态——当前决策第i个物品(如果不是要backtrack出所用的物品,这个状态无需表达出来,可以直接用滚动数组实现)

第二个状态——当前选择了j个物品

第三个状态——选择j件物品后,两种属性的差值是k

dif —— 第一种属性减第二种的差值

sum —— 两种属性的和

状态转移方程是 dp[i][j][k] = max(dp[i - 1][j][k], dp[i - 1][j - 1][k - dif] + sum)

代码:(两部分都贴出来,用宏做开关....另外,我把debug也放进去了,所以看起来会比较长)

View Code

#include <cstdio>
#include <cstring>
#include <cmath>

#define debug 0
#define prog 1//1是较高效的代码 2是超时的代码

int min2(int _a, int _b){return _a < _b ? _a : _b;}
int max2(int _a, int _b){return _a > _b ? _a : _b;}

int s[20], top;
int d[201], p[201];
#if prog == 1

int dp[201][21][1001];
const int inf = 100000000;
const int mid = 500;//这里至少要420,这样下面的代码就不需要分类讨论了

int main(){
int n, m;
int c = 1;

while (~scanf("%d%d", &n, &m) && (n || m)){
for (int k = 0; k <= n; k++){
for (int i = 0; i <= m; i++){
for (int j = 0, endj = mid << 1; j <= endj; j++){
dp[k][i][j] = -inf;
}
}
dp[k][0][mid] = 0;
}
for (int i = 1; i <= n; i++){
scanf("%d%d", &d[i], &p[i]);
for (int j = 1, endj = min2(i, m); j <= endj; j++){
int dif = d[i] - p[i];
int sum = d[i] + p[i];
for (int k = mid - 400, endk = mid + 400; k <= endk; k++){
dp[i][j][k] = max2(dp[i - 1][j][k], dp[i - 1][j - 1][k - dif] + sum);
}
}
}

bool found = false;
int mi = 0, mj = 0;

for (int i = 0; i <= 400 && !found; i++){
if (dp
[m][mid + i] > 0){
found = true;
mj = i;
mi = dp
[m][mid + i];
}
if (dp
[m][mid - i] > 0){
found = true;
if (mi < dp
[m][mid - i]){
mj = -i;
mi = dp
[m][mid - i];
}
}
}
#if debug
for (int i = 0; i <= m; i++){
for (int j = mid - 5; j <= mid + 5; j++){
printf("%d ", dp
[i][j]);
}
puts("");
}
printf("mi %d  mj %d\n", mi, mj);
puts("");
printf("%d %d\n", (mi + mj) >> 1, (mi - mj) >> 1);
#endif
printf("Jury #%d\nBest jury has value %d for prosecution and value %d for defence:\n", c, (mi + mj) >> 1, (mi - mj) >> 1);
int t = n;
top = 0;
mj += mid;

for (; t && mj && m; t--){//刚开始m忘记判断了,所以RE了几次,还以为是数组不够大搞到我不停开大数组
int dif = d[t] - p[t];
int sum = d[t] + p[t];

if (dp[t][m][mj] == dp[t - 1][m - 1][mj - dif] + sum){
#if debug
puts("pass");
#endif
m--;
mj -= dif;
s[top++] = t;
}
}//backtrack只要直接找到满足更新条件的两个数就可以了
c++;
for (int i = top - 1; i >= 0; i--){
printf(" %d", s[i]);
}
puts("");
puts("");
}

return 0;
}

#endif

#if prog == 2
bool dp[21][401][401];

int main(){
int n, m;
int c = 1;
int sumd, sump;

while (~scanf("%d%d", &n, &m) && (n || m)){
memset(dp, 0, sizeof(dp));
dp[0][0][0] = true;
sumd = sump = 0;
for (int i = 1; i <= n; i++){
scanf("%d%d", &d[i], &p[i]);
sumd += d[i];
sumd = min2(sumd, 400);
sump += p[i];
sump = min2(sump, 400);
for (int l = min2(i, m); l >= 1; l--){
for (int j = sumd; j >= d[i]; j--){
for (int k = sump; k >= p[i]; k--){
dp[l][j][k] |= dp[l - 1][j - d[i]][k - p[i]];
}
}
}
}

bool found = false;
int mi = 0, mj = 0;

for (int t = 0; t <= 400 && !found; t++){
for (int i = 400; i >= t && !found; i--){
if (dp[m][i][i - t]){
found = true;
mi = i;
mj = i - t;
}
else if (dp[m][i - t][i]){
found = true;
mi = i - t;
mj = i;
}
}
}
#if debug
for (int i = 0; i <= 20; i++){
for (int j = 0; j <= 20; j++){
printf("%d", dp[1][i][j]);
}
puts("");
}
printf("i %d j %d\n", mi, mj);
#endif

printf("Jury #%d\nBest jury has value %d for prosecution and value %d for defence:\n", c, mi, mj);
c++;
top = 0;
while (mi && mj && m){
for (int i = n; i >= 1; i--){
if (mi - d[i] >= 0 && mj - p[i] >= 0 && dp[m - 1][mi - d[i]][mj - p[i]]){
s[top++] = i;
mi -= d[i];
mj -= p[i];
m--;
}
}
}
for (int i = top - 1; i >= 0; i--){
printf(" %d", s[i]);
}
puts("");
}

return 0;
}
#endif


  Work Harder!Work for My Better Life!  

--Written by Lyon
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: