首页 > 代码库 > dp1015

dp1015

 

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

 

我觉得最难的地方在于用二维数组抽象问题,来表示问题的最优解。

这种问题中,包含了两个因素来决定最优解:差最小、和最大,因此用二维数组表示最合适。同时递归最小解过程中,个数必须考虑(以此递归),因而个数也要加上去。

 

此题与背包问题相似的地方在于都是对于拿取的顺序无要求,所以取了第I个后,遍历所有第I个的所有状态【J】,得到第I+1的结果。

与背包不同的地方在于这里对于个数是确定的,并不是可以取任意多个

for (int i=0; i<=m-1; i++)这层是确定个数
for (int j=0; j<=800; j++)这层是对于某个I遍历所有状态

for (int l=1; l<=n && dp[i][j]>=0; l++)这层循环就是为了防止重复

 

并且,我们还规定,如果没法选个人,使其辩控差为k,那么dp(j, k)的值就为-1,也称方案dp(j, k)不可行

为什么fix=400?这是很显然的,m上限为20人,当20人的d均为0p均为20时,会出现辨控差为-400。修正后回避下标负数问题,区间整体平移,从[-400,400]映射到[0,800]

此时初始条件修正为dp(0, fix) = 0,其他均为-1

 

 if (dp[i+1][j+num[l].d-num[l].p]<dp[i][j]+num[l].d+num[l].p)

替换原先DP[I+1][J]中,总分之和比较小的(小于第I+1个元素 加进来之后的总分和)

 

 

83        while (dp[m][i+k]<0 && dp[m][i-k]<0) k++;            //求出最小的差值

实际是应该是求D分数和与P分数和之差的绝对值,但前面统一用D-P求值,因此只需要在最后考虑加绝对值。即根据题意,差值应该是个正值(取绝对值),但因为一直用D-P,所以可能变成负的,因此可能最终的结果在400+k,中,也可能在400-k中,需要比较。

 

#include <iostream>

 #include <cstdio>

 #include <cstring>

 #include <cmath>

 #include <string>

#include <algorithm>

 

 using namespace std;

 

struct player

{

    int d,p;

}num[300];              //表示控方和辩方分别给的分数

int result[300];        //储存最终的m个陪审团成员

 int n,m,x,y,xx,yy;

 int path[30][1000];     //记录选择i个人,以差值j结尾的陪审团成员的最后一个人

 int dp[30][1000];       //记录选择i个人,差值为j的最大和是多少

 

 void init()

 {

     memset(path,0,sizeof(path));

     memset(result,0,sizeof(result));

     for (int i=0; i<=m; i++)

       for (int j=0; j<=800; j++)

       {

           dp[i][j]=-1;

       }

     dp[0][400]=0;                   //什么都没有的时候,即为0400是贯穿整个程序的,目的是为了使差值由负变正,可以在数组在中储存

 }

 

 void get_dp()

 {

     for (int i=0; i<=m-1; i++)

     {

         for (int j=0; j<=800; j++)

         {

             for (int l=1; l<=n && dp[i][j]>=0; l++)

             {

                 if (dp[i+1][j+num[l].d-num[l].p]<dp[i][j]+num[l].d+num[l].p)//左边是在dp[i][j]基础上不加i+1的情况,后者是加了后的情况

                 {

                     x=i;

                     y=j;

                     while(x>0&&path[x][y]!=l)//不是x>=0

                     {

                        y-=num[path[x][y]].d-num[path[x][y]].p;       //判断这个新加入的l在原来的成立的陪审团中是否存在

                        x--;

                    }

                    if(x==0)

                    {

                        dp[i+1][j+num[l].d-num[l].p]=dp[i][j]+num[l].d+num[l].p;

                        path[i+1][j+num[l].d-num[l].p]=l;

                    }

                }

            }

        }

    }

}

 

int cmp(int a,int b)

{

    return a<b;

}

 

int main()

{

    int T=0,i;

FILE *fp=fopen("1.txt","r");

 

while(fscanf(fp,"%d%d",&n,&m))

 

  //  while (~scanf("%d%d",&n,&m))

    {

        T++;

        if (n==0 && m==0) break;

        init();                     //初始化

        int xx,yy;

        for ( i=1; i<=n; i++)

        {

            fscanf(fp,"%d%d",&xx,&yy);

            num[i].d=xx;

            num[i].p=yy;

        }

        get_dp();                             //dp[i][j]表示的是选择i个人,差值为j的最大和是多少

        int k=0;

        int i=400;                            //400是贯穿整个程序的,目的是为了使差值由负变正,可以在数组在中储存

        int temp;

        while (dp[m][i+k]<0 && dp[m][i-k]<0) k++;            //求出最小的差值

 

 

        //找出m个人的陪审团成员,和统计m个人的陪审团控方得分和和辩方得分和

        if (dp[m][i+k]>dp[m][i-k]) temp=i+k;

            else temp=i-k;

        int x=y=0;

         for( i=0; i<=m-1; i++)

        {

            result[i]=path[m-i][temp];

            x+=num[result[i]].d;

            y+=num[result[i]].p;

            temp-=num[result[i]].d-num[result[i]].p;

        }

 

        //题目要求从小到大输出~第一次就WA在这里,注意审题

        sort(result,result+m,cmp);

 

        cout << "Jury #" << T << endl;

        cout << "Best jury has value " << x << " for prosecution and value " << y << " for defence:" << endl;

        for ( i=0; i<=m-1; i++)

            cout << " " << result[i];

        cout << endl;

    }

    return 0;

}