• POJ 1015 Jury Compromise(dp坑)


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

    题解:开始想到的是二维01背包,因为评价差的总分值最大可能就只有[-400,400],所以我们整体加上20*m就可以直接放入dp的一维来解决

         开两个数组:

       path[i][j];//选i个人的评价差为j时最后一个人的编号

         dp [i][j];//选i个人的评价差为j时最大评价和

       然后使用三重循环:第一重是枚举选择1到m个人,第二重是枚举参选的人,第三重是枚举评价差的总可能值,枚举这个人去参选每种评价差的情况

       然后判断当前这个人在这个位置时是否已经被选过了与现在计算的评价和是否大于存储的(因为评价差固定了)评价和

       接着就是注意初始化要在加20*m的位置初始化(关键),而不是(0,0)

       但是这样有bug,例如:选择135与选择246的评价差与评价和是一样的,我们可能选择了246把135忽略掉了,但是正确答案却是1356

       因此还有这个办法(网上看到的):我们把循环的位置换一下,将第二重循环放在第一重,原来第一重放在第二重,其他的不变

       这样只有固定每个参选人后才可以枚举下一个参选人,就解决这个bug了

       接下来如果把第二重循环倒叙就可以保证不会出现重复的人,这样就不用判断这个人是否之前已经被选择过(类似01背包)

       最后要注意循环顺序改变后不能回溯找路了,因为第一重循环会把路径打乱,所以我们得存储路径

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<vector>
    #include<string>
    #include<cstdio>
    #include<cstring>
    #include<iomanip>
    #include<stdlib.h>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define eps 1E-8
    /*注意可能会有输出-0.000*/
    #define sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型
    #define cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化
    #define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0
    #define mul(a,b) (a<<b)
    #define dir(a,b) (a>>b)
    typedef long long ll;
    typedef unsigned long long ull;
    const int Inf=1<<28;
    const ll INF=1LL<<60;
    const double Pi=acos(-1.0);
    const int Mod=1e9+7;
    const int Max=440;
    struct node
    {
        int d,p;
        int sum,sub;
    }que[Max];
    vector<int> path[25][Max<<1];//选i个人的评价差为j时最后一个人的编号
    int dp[25][Max<<1];//选i个人的评价差为j时最大评价和
    int now[25][Max<<1];//存值便于回溯
    int ansp,ansd,ans[25];
    int coun;
    void dfs(int i,int j)
    {
        coun=0;
        int siz=path[i][j].size();
      for(int k=0;k<siz;++k)
      {
          ans[coun++]=path[i][j][k];
      }
      return;
    }
    void Solve(int n,int m)
    {
        ansd=ansp=0;
        int fix=20*m;//右移fix
        memset(dp,-1,sizeof(dp));//不允许走的地方
        for(int i=0;i<=m;++i)
            for(int j=0;j<2*fix;++j)
        path[i][j].clear();
        dp[0][fix]=0;//向右移动fix保证所有为非负
        for(int i=0;i<n;++i)//确定人后再确定下一人,这样避免出现当评论差与评论和都相同却不能存的情况
        {
            for(int j=m;j>0;--j)//选择的人数,倒叙保证不重复(01背包)
            {
                for(int k=0;k<2*fix;++k)//评论差的值
                {
                    int kk=k-que[i].sub;
                    if(kk>=0&&dp[j-1][kk]>=0&&dp[j][k]<dp[j-1][kk]+que[i].sum)
                    {
                        dp[j][k]=dp[j-1][kk]+que[i].sum;
                        path[j][k]=path[j-1][kk];//因为遍历m个人的循环在内部,所以不能回溯找路径
                        path[j][k].push_back(i);
                    }
                }
            }
        }
        for(int i=fix,j=fix;i>=0;--i,++j)
        {
            if(dp[m][i]>=0)
            {
                if(dp[m][j]>dp[m][i])
                    dfs(m,j);//找到(减去fix后的绝对值)最接近0的值
                else
                    dfs(m,i);
                break;
            }
            if(dp[m][j]>=0)
            {
                dfs(m,j);
                break;
            }
        }
        for(int i=0;i<m;++i)
        {
            ansp+=que[ans[i]].p;
            ansd+=que[ans[i]].d;
        }
        return;
    }
    int main()
    {
        int n,m;
        int coun=0;
        while(~scanf("%d %d",&n,&m)&&(n||m))
        {
            for(int i=0; i<n; ++i)
            {
                scanf("%d %d",&que[i].d,&que[i].p);
                que[i].sum=que[i].d+que[i].p;
                que[i].sub=que[i].d-que[i].p;
            }
            Solve(n,m);
            printf("Jury #%d
    ",++coun);
            printf("Best jury has value %d for prosecution and value %d for defence:
    ",ansd,ansp);
            for(int i=0; i<m; ++i)
                printf(" %d",ans[i]+1);
            printf("
    
    ");
        }
        return 0;
    }

      

  • 相关阅读:
    广告效果滚动
    判断背景图片是否加载成功
    css3创建多边形clip属性,可用来绘制不规则图形了
    不允许用户选中文本的两种方法
    栈类模板设计及应用
    HDOJ 题目类型
    极大团数量模板
    HDU 1522 Marriage is Stable 稳定婚姻匹配
    字符串类设计与应用
    正向与反向拓扑排序的区别(hdu 1285 确定比赛名次和hdu 4857 逃生)
  • 原文地址:https://www.cnblogs.com/zhuanzhuruyi/p/6390409.html
Copyright © 2020-2023  润新知