POJ 1015

题意:

在遥远的国家佛罗布尼亚,嫌犯是否有罪,须由陪审团决定。陪审团是由法官从公众中挑选的。先随机挑选n 个人作为陪审团的候选人,然后再从这n 个人中选m 人组成陪审团。选m 人的办法是:控方和辩方会根据对候选人的喜欢程度,给所有候选人打分,分值从0 到20。为了公平起见,法官选出陪审团的原则是:选出的m 个人,必须满足辩方总分D控方总分P的差的绝对值|D-P|最小。如果有多种选择方案的 |D-P| 值相同,那么选辩控双方总分之和D+P最大的方案即可。

输出:

选取符合条件的最优m个候选人后,要求输出这m个人的辩方总值D和控方总值P,并升序输出他们的编号。

思路:

动态规划,修正值那里还挺神奇的,最开始没写出来,参照了别人的代码(http://blog.csdn.net/lyy289065406/article/details/6671105)

具体思路见代码注释。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<stack>
#include<algorithm>
#include<vector>
#define y1 y12345
#define mx 205
#define inf 0x3f3f3f3f
#define LL long long
#define ULL unsigned long long
#define mod 1000000007
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;

struct point{
    int d;   //控方
    int p;   //辩方
    int v;   //d-p(控辩差)
    int s;   //d+p
}a[mx];
int dp[25][805];     //dp[j][k]:取j个候选人,使其控辩差为k的所有方案中,控辩和最大的方案的控辩和
int pre[25][8058];   //记录dp[j][k]的前驱
int b[mx];

bool haha(int j,int k,int id){     //判断dp[j][k]方案是否选取过候选人i
    while(j){
       int i=pre[j][k];
       if(i==id)return false;
       j--;
       k=k-a[i].v;
    }
    return true;
}

int main(){
   int n,m;
   int ca=1;
   while(scanf("%d %d",&n,&m)&&n&&m){
       for(int i=1;i<=n;i++){
           scanf("%d %d",&a[i].p,&a[i].d);
           a[i].v=a[i].d-a[i].p;
           a[i].s=a[i].d+a[i].p;
       }

       const int fix=20*m;          //总修正值,修正极限为从[-400,400]映射到[0,800]
       memset(dp,-1,sizeof(dp));    //dp值为-1代表dp[k][k]方案不合法
       memset(pre,0,sizeof(pre));
       dp[0][fix]=0;                //dp[0][fix]为真正的dp[0][0]
       //dp
       for(int j=1;j<=m;j++){
           for(int k=0;k<=2*fix;k++){
               if(dp[j-1][k]!=-1){
                   for(int i=1;i<=n;i++){
                       if(haha(j-1,k,i)){
                           if(dp[j][k+a[i].v]<dp[j-1][k]+a[i].s){
                               dp[j][k+a[i].v]=dp[j-1][k]+a[i].s;
                               pre[j][k+a[i].v]=i;
                           }
                       }
                   }
               }
           }
       }

       //从中间向两边搜索寻找最小的控辩差
       int ans=-1,ans1=-1,ans2=-1;
       for(int k=0;k<=fix;k++){
           if(dp[m][fix-k]!=-1)ans1=fix-k;
           if(dp[m][fix+k]!=-1)ans2=fix+k;
           if(ans1!=-1&&ans2!=-1){
               if(dp[m][ans1]>dp[m][ans2])ans=ans1;
               else ans=ans2;
               break;
           }
           else if(ans1!=-1){ans=ans1;break;}
           else if(ans2!=-1){ans=ans2;break;}
       }

       //b[i]记录路径
       ans1=ans2=0;
       for(int i=m;i>0;i--){
           b[i]=pre[i][ans];
           ans1+=a[b[i]].p;
           ans2+=a[b[i]].d;
           ans=ans-a[b[i]].v;
       }
       sort(b+1,b+1+m);
       //输出
       printf("Jury #%d\nBest jury has value %d for prosecution and value %d for defence:\n",ca++,ans1,ans2);
       for(int i=1;i<=m;i++)printf(" %d",b[i]);
       cout<<endl<<endl;
   }
   return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值