1. 程式人生 > >動態規劃之Jury Compromise

動態規劃之Jury Compromise

Jury Compromise

In Frobnia, a far-awaycountry, the verdicts in court trials are determined by a jury consisting ofmembers of the general public. Every time a trial is set to begin, a jury hasto be selected, which is done as follows. First, several people are drawnrandomly from the public. For each person in this pool, defence and prosecutionassign a grade from 0 to 20 indicating their preference for this person. 0means total dislike, 20 on the other hand means that this person is consideredideally suited for the jury.

Based on the grades of the two parties,the judge selects the jury. In order to ensure a fair trial, the tendencies ofthe jury to favour either defence or prosecution should be as balanced aspossible. The jury therefore has to be chosen in a way that is satisfactory toboth parties.

We will now make this more precise: givena pool of n potential jurors and two values di (the defence's value) and pi(the prosecution's value) for each potential juror i, you are to select a juryof m persons. If J is a subset of {1, ..., n} with m elements, then D(J) = sumdk (k in J) and P(J) = sum pk (k in J) are the total values of this jury fordefence and prosecution.

For an optimal jury J, the value | D(J) -P(J) | must be minimal. If there are several jurys with minimal | D(J) - P(J)|, one which maximizes D(J) + P(J) should be selected since the jury should beas ideal as possible for both parties.

You are to write a program that implementsthis jury selection process and chooses an optimal jury given a set ofcandidates.

Note: If your solution is based on aninefficient algorithm, it may not execute in the allotted time.


Input

The input contains several jury selection rounds. Each round starts with a linecontaining two integers n and m. n is the number of candidates and m the numberof jury members. These values will satisfy 1 <= n <= 200, 1 <= m <=20 and of course m <= n. The following n lines contain the two integers piand di for i = 1, ..., n. A blank line separates each round from the next.

The input ends with a round that has n = m= 0.


Output

For each round output a line containing the number of the jury selection round(`Jury #1', `Jury #2', etc.).

On the next line print the values D(J) andP(J) of your jury as shown below and on another line print the numbers of the mchosen candidates in ascending order. Output a blank before each individualcandidate number.

Output an empty line after each test case.


Sample Input

4 2
1 2
2 3
4 1
6 2

0 0


Sample Output

Jury #1
Best jury has value 6 for prosecution and value 4 for defence:
 2 3

題目意思:體重給出n(1<=n<=200)個候選人,每個人都有兩種屬性d和p(他們都位於1到20 之間),從中選出m(1<=m<=20,且m<=n)個人。要求這m個人滿足他們全部d的和減去全部p的和的絕對值最小,如果最小值不止一個,那麼久選取全部d的和加上全部p的和最大的那一個。

解題思路:這題很顯然要用到動態規劃。為了便於描述,我們經d和p的屬性放在一個結構體Node中。對於選m個人,∑p-∑d的取值範圍是[-20*m,20*m]。用key[i][j]表示從n個人中選出i個,他們的∑p-∑d=j且∑p+∑d最大。如果key[i][j]=-1表示不存在這樣的組合,key[i][j]=0是動態規劃的起點。則動態規劃的轉移方程為:

如果key[i-1][s]是一個有效的組合,且結點t不在key[i-1][s]中,

則key[i][s+node[t].p-node[t].d]也是一個有效的組合,如果key[i][s+node[t].p-node[t].d]<key[i-1][s]+node[t].p+node[t].d ,那麼就更新。由於在陣列中的下標不可能取負數,所以將下標進行一個對映,使其全部非負(下標全部加400即可)。為了判斷結點t是否與在key[i-1][s]中出現過,我們需要一個數組path來記錄結點出現的順序。path的維數與key相同。path[i][j]表示滿足key[i][j]出現的第一個元素。下一個元素為path[i-1][j-(node[path[i][j]].p- node[path[i][j]].d)。path[i][j]=0則說明不存在路徑。

所以具體的程式碼如下:

#include<iostream>

#include<algorithm>

using namespace std;

#define maxn 300  //候選人人數的最大值

#define maxm 30   //陪審團人數的最大值

struct Node

{

         intd;

         intp;

};

Node node[maxn];//存放著候選人的分數

int key[maxm][40*maxm];//key[i][j]表示從候選人中選取i個,其∑(d-p)=j且∑(d+p)最大的值

int path[maxm][40*maxm];//path[i][j]存放著當key[i][j]為可行值時所存放著路徑

int result[maxm];//存放最終結果

int n,m;

void dp();

int main()

{

         intcasenum=1;

         cin>>n>>m;

         while((n!=0)&&(m!=0))

         {

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

                   {

                            cin>>node[i].p>>node[i].d;

                   }

                   cout<<"Jury#"<<casenum<<endl;

                   casenum+=1;

                   dp();

                   cin>>n>>m;

         }

         return0;

}

void dp()

{

         intmaxX=m*20;//選取m個人中差值最大的值

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

         {

                   for(intj=0;j<=2*maxX;j++)

                   {

                            key[i][j]=-1;//-1表示沒有可行方案

                            path[i][j]=0;//0表示沒有路徑或者路徑到此結束

                   }

         }

         key[0][maxX]=0;//dp起點.

         for(inti=1;i<=m;i++)

         {

                   for(intj=0;j<=2*maxX;j++)

                   {

                            if(key[i-1][j]>=0)//可行方案才進行組合

                            {

                                     for(intk=1;k<=n;k++)

                                     {

                                               if(key[i-1][j]+node[k].d+node[k].p>key[i][j+node[k].p-node[k].d])

                                               {

/*注意:j+node[k].p-node[k].d]之所以不會越界是因為當j的有效範圍是[-20*(i-1),20*(i-1)],就算在兩端加上(±)20也不會越界*/

                                                        intt1,t2;

                                                        t1=i-1;

                                                        t2=j;

                                                        while((t1>=0)&&(path[t1][t2]!=k))

                                                        {

                                                                 //t2=path[t1][t2];

                                                                 t2-=node[path[t1][t2]].p-node[path[t1][t2]].d;

                                                                 t1-=1;

                                                        }

                                                        if(t1==-1)

                                                        {

                                                                 key[i][j+node[k].p-node[k].d]=key[i-1][j]+node[k].d+node[k].p;

                                                                 path[i][j+node[k].p-node[k].d]=k;

                                                        }

                                               }

                                     }

                            }

                   }

         }       

         intmid=maxX;

         intincrease=0;

         intlast;

         while((key[m][mid+increase]<0)&&(key[m][mid-increase]<0))

         {

                   increase+=1;

         }

         if(key[m][mid+increase]>key[m][mid-increase])

                   last=mid+increase;

         else

                   last=mid-increase;

         cout<<"Bestjury has value "<<(key[m][last]+last-mid)/2<<" forprosecution and value "<<(key[m][last]-last+mid)/2<<" fordefence:"<<endl;

         intst=last;

         for(inti=m;i>0;i--)

         {

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

                   st-=node[path[i][st]].p-node[path[i][st]].d;

         }

         sort(result,result+m);

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

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

         cout<<endl<<endl;

}