POJ 1015

中文题意:

描述在遥远的国家佛罗布尼亚,嫌犯是否有罪,须由陪审团决定。陪审团是由法官从公众中挑选的。先随机挑选n个人作为陪审团的候选人,然后再从这n个人中选m人组成陪审团。选m人的办法是:

控方和辩方会根据对候选人的喜欢程度,给所有候选人打分,分值从0到20。为了公平起见,法官选出陪审团的原则是:选出的m个人,必须满足辩方总分和控方总分的差的绝对值最小。如果有多种选择方案的辩方总分和控方总分的之差的绝对值相同,那么选辩控双方总分之和最大的方案即可。
输入输入包含多组数据。每组数据的第一行是两个整数n和m,n是候选人数目,m是陪审团人数。注意,1<=n<=200, 1<=m<=20 而且 m<=n。接下来的n行,每行表示一个候选人的信息,它包含2个整数,先后是控方和辩方对该候选人的打分。候选人按出现的先后从1开始编号。两组有效数据之间以空行分隔。最后一组数据n=m=0输出对每组数据,先输出一行,表示答案所属的组号,如 'Jury #1', 'Jury #2', 等。接下来的一行要象例子那样输出陪审团的控方总分和辩方总分。再下来一行要以升序输出陪审团里每个成员的编号,两个成员编号之间用空格分隔。每组输出数据须以一个空行结束。

样例输入

4 2 
1 2 
2 3 
4 1 
6 2 
0 0 

样例输出

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

 

 

 

解析:

我们很容易发现这题是一个01背包,但是如果直接设dp[i][j][k]为当前选了i个人,差值为j,和为k是否可行的话复杂度为n*m*400*400(枚举总人数,当前选到第几个人,差值为多少,和为多少的情况),显然是不可行的。

这个时候需要用到一个dp的常见技巧,就是把dp的一维放到dp数组的值里面,本来这个dp是四维的(有一维被滚动数组优化掉了),我们现在把dp状态设为dp[j][k]表示选了j个人,差为k的时候的最大和, 这样我们就把dp变成了3维,时间复杂度够了,用如下方程转移:

POJ 1015

 

 注意这个本质上是一个01背包滚动数组优化,所以我们枚举j的时候需要倒序枚举

这样我们就可以通过枚举差值来更新答案,最后得到最小差值ans

接下来题目要求输出方案,我们用pa[i][j][k]表示进入到枚举了i个人,选出了j个人,差为k这个状态是因为选了哪一个人,这样的话因为我们知道末状态一定是枚举了n个人,选出了m个人,差值为ans,所以我们可以倒推回去:比如我们现在是在i,j,k这个状态,而pa[i][j][k]=pre,那么我们就知道上一个状态是pre-1,j-1,k-(p[pre]-d[pre]),尝试着模拟一下样例应该就能理解了。

 

注意点:每输出一个方案后要多空一行,方案开头要空一个格,另外,转移时需要加一个偏移值防止出现负数数组索引

 

#include<iostream>
#include<string.h>
#include<math.h>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
#include<stack>
using namespace std;
typedef long long ll;
const int fix=400;

int dp[21][801],pa[201][21][801],p[201],d[201],sp,sd;
vector<int> a;

void pt(int n,int m,int now){
    if (m==0) return;
    a.push_back(pa[n][m][now+fix]);
    int tmp=pa[n][m][now+fix];
    sp+=p[tmp];
    sd+=d[tmp];
    pt(tmp-1,m-1,now-p[tmp]+d[tmp]);
}

int main(){
    int n,m,cs=0;
    while (cin>>n>>m,n,m){
        memset(dp,0xcf,sizeof dp); 
        dp[0][fix]=0;
        a.clear();
        sp=sd=0;
        ++cs;
        for (int i=1;i<=n;i++) cin>>p[i]>>d[i];
        for (int i=1;i<=n;i++){
            for (int j=0;j<=m;j++){//这里是预先假设当前第i个人不选 将此时的pa设为上个人对应状态的pa 
                for (int k=-400;k<=400;k++)  pa[i][j][k+fix]=pa[i-1][j][k+fix];
            }
            for (int j=m;j>=1;j--){
                for (int k=-400;k<=400;k++){
                    if (k-p[i]+d[i]<-400 || k-p[i]+d[i]>400) continue;
                    if (dp[j][k+fix]<dp[j-1][k-p[i]+d[i]+fix]+p[i]+d[i]){
                        dp[j][k+fix]=dp[j-1][k-p[i]+d[i]+fix]+p[i]+d[i];
                        pa[i][j][k+fix]=i;
                    }
                }
            }
        }
        int ans=400;
        for (int i=-400;i<=400;i++){
            if (dp[m][i+fix]<0) continue;
            if (abs(i)<abs(ans) || abs(i)==abs(ans) && dp[m][i+fix]>dp[m][ans+fix]) ans=i; 
        }
        cout<<"Jury #"<<cs<<endl;
        //cout<<ans<<endl; 
        pt(n,m,ans);
        printf("Best jury has value %d for prosecution and value %d for defence:\n",sp,sd);
        reverse(a.begin(),a.end());
        for (int i=0;i<a.size();i++) cout<<" "<<a[i];
        cout<<endl<<endl;
    }
    return 0;
}
上一篇:vue用命令批量解决ESLint警告


下一篇:399330深证100指数的一个策略