POJ 1015 Jury Compromise dp 01背包 输出路径

http://poj.org/problem?id=1015

题意: 两个数x, y初始是0, 共有n个操作, 每个操作向x加a, y加b, 要求选出恰好m次操作, 使得x和y的差最小, 在此基础上, 令x+y最大, 输出最终x和y的值, 并按升序输出都取了那几个位置的数.

思路:双塔dp, dp[i][j]中i表示已经取了i个数, j表示x和y当前的差是多少.

所以, 枚举取了几个数→枚举差值→枚举下一个选的数.

注意:

1: 差值可能是负的, 要加一个数(fix)将范围移动到0~x之间.

2: 初始化dp数组要为-1, 表示当前此状态不可达, 否则(赋0的话)会出现"本来这个状态到不了, 却被当作值为0的可达状态"的情况.

3: 用dp[i-1][j]+a+b更新dp[i][j+a-b]        和       用dp[i-1][j-(a-b)]+a+b更新dp[i][j]        是不同的:

后者是我的代码, 要注意控制j>a-b&&j-a+b<=fix*2(显然fix*2是能达到的最高值了, 再高没有意义,那些状态不可能达到, 自然也没有子状态, 我空间开小了, 又没有加限定条件, 开了805访问到820了)

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<iostream>
#define fuck(x) std::cout<<"["<<#x<<"->"<<x<<"]"<<endl;
using namespace std;
typedef long long ll;

const int M=205;
const int inf=1e9+7;

int n,m;
int dp[205][805];
int path[205][805];
struct node {
    int a,b;
} pep[M];
int fix;

bool hv_used(int i,int j,int k) {
    while(1) {
        if(i==0)
            return 0;
        if(path[i][j]==k)
            return 1;
        j-=pep[  path[i][j]  ].a-pep[  path[i][j]  ].b;
        i--;
    }
}

void output() {
    int pos;
    for(int i=0; i<=fix; i++) {
        if(dp[m][fix+i]>=0&&dp[m][fix+i]>=dp[m][fix-i]) {
            pos=fix+i;
            break;
        }
        if(dp[m][fix-i]>=0&&dp[m][fix-i]>=dp[m][fix+i]) {
            pos=fix-i;
            break;
        }
    }
    printf("Best jury has value %d for prosecution and value %d for defence:\n"
           ,(dp[m][pos]+pos-fix)/2,(dp[m][pos]-pos+fix)/2);
    vector<int>s;
    int ii=m,jj=pos;
    while(1) {
        if(ii==0)
            break;
        s.push_back(path[ii][jj]);
        jj-=pep[  path[ii][jj]  ].a-pep[  path[ii][jj]  ].b;
        ii--;
    }
    sort(s.begin(),s.end());
    for(int i=0; i<s.size(); i++) {
        printf(" %d",s[i]);
    }
    printf(" ");
    puts("");
    puts("");
}

int main() {
    int caz=0;
    while(~scanf("%d%d",&n,&m)&&(n||m)) {
        caz++;
        for(int i=1; i<=n; i++) {
            scanf("%d%d",&pep[i].a,&pep[i].b);
        }
        printf("Jury #%d\n",caz);
        fix=m*20;
        memset(dp,-1,sizeof(dp));
        memset(path,0,sizeof(path));
        dp[0][fix]=0;
        for(int i=1; i<=m; i++) {
            for(int j=0; j<=2*fix; j++) {

                for(int k=1; k<=n; k++) {
                    int a=pep[k].a,b=pep[k].b;
                    if(j>=a-b&&j-a+b<=2*fix
                            &&dp[i-1][j-(a-b)]>=0//这里最高会访问j-(a-b)=820位置的数据,所以不能开805
                            &&dp[i][j]<=dp[i-1][j-(a-b)]+a+b
                            &&hv_used(i-1,j-(a-b),k)==0) {
                        dp[i][j]=dp[i-1][j-(a-b)]+a+b;
                        path[i][j]=k;
                    }
                }
            }
        }

        output();
    }
    return 0;
}





/*
21 13
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
11 11
12 12
13 13
14 14
15 15
16 16
17 17
18 18
19 19
20 20
*/

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值