100道动态规划——23 POJ 1015 Jury Compromise 恩,我也不知道怎么说这里的知识点

直接切入正题吧,我觉得这个题和我这里的http://blog.csdn.net/good_night_sion_/article/details/53426599第16道动态规划是类似的。

我想的状态表示是dp[i][j][k]表示前i个人选了j个差为k的状态是不是可达,给每一个人记录编号,然后按照控方+辩方分数之和从小到大排序,最后找解的之后贪心优先选择后面的,好吧,在写完这句话的这一刻我就意识到我的写法错了。

AC的写法是定义状态dp[i][k]表示选了i个人差为k时的最大控方+辩方分数

因此状态转移方程就是dp[i+1][k+第j个人控方分-第j个人辩方分]=dp[i][k]+第j个人控方分+第j个人辩方分,条件是第j个人没有使用过

然后要重构解的话,要定义一个额外的数组record表示这个状态是从哪里转移过来的,当然我的意思不是只要重构解就要定义一个新数组

代码应该是很好懂的,对了,在POJ上交的话,不能用我的那个for_each语句,需要自己写一个for循环来输出,POJ不支持lambda表达式...

TIPS:judge用于判断状态为i,k的时候第j个人有没有用过,getans用于把人放到ans里面去

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;
typedef pair<int,int> pa;

pa arr[205];
vector<int> ans;
int dp[25][805],record[25][805],n,m,a,b,kcase=1;
bool judge(int i,int j,int k);
void getans(int k);

int main(){
    while(scanf("%d%d",&n,&m)&&n+m){
        for(int i=0;i<n;++i)
            scanf("%d%d",&arr[i].first,&arr[i].second);

        memset(dp,-1,sizeof dp);
        memset(record,-1,sizeof record);

        dp[0][m*5<<2]=0;//m*20

        for(int i=0;i<m;++i)
        for(int k=0;k<=((m*5)<<3);++k)//k<=m*40
        if(dp[i][k]!=-1)
        for(int j=0;j<n;++j)
        if(dp[i][k]+arr[j].first+arr[j].second>dp[i+1][k+arr[j].first-arr[j].second]&&judge(i,j,k))
            dp[i+1][k+arr[j].first-arr[j].second]=dp[i][k]+arr[j].first+arr[j].second,record[i+1][k+arr[j].first-arr[j].second]=j;

        for(int te=((m*5)<<2),k=0;k<=te;++k)
        if(dp[m][te+k]!=-1&&dp[m][te-k]!=-1){
            if(k==0)
                getans(te);
            else
                getans((dp[m][te+k]>dp[m][te-k])?te+k:te-k);
            break;
        }
        else if(dp[m][te+k]!=-1){
            getans(te+k);
            break;
        }
        else if(dp[m][te-k]!=-1){
            getans(te-k);
            break;
        }

        printf("Jury #%d\nBest jury has value %d for prosecution and value %d for defence:\n",kcase++,a,b);
        for_each(ans.begin(),ans.end(),[](int k){printf(" %d",k+1);});
        printf("\n\n");
        ans.clear();
        a=b=0;
    }
    return 0;
}

bool judge(int i,int j,int k){
    int te;
    while(record[i][k]!=-1){
        te=record[i][k];
        if(te==j)
            return false;
        k+=arr[te].second-arr[te].first,--i;
    }
    return true;
}

void getans(int k){
    int i=m,te;
    while(record[i][k]!=-1){
        ans.push_back(te=record[i][k]);
        a+=arr[te].first,b+=arr[te].second;
        k+=arr[te].second-arr[te].first,--i;
    }
    sort(ans.begin(),ans.end());
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值