2019.9.29 陪审团
陪审团制度是由法官从公民中选出N人作为陪审团候选人。然后由原被告双方从中选出M人组成陪审团,由陪审团决定是否定罪。
首先,参与诉讼的双方给所有候选人打分,分值在0到20之间。第i个人得分分别记为a[i]和b[i]。为了公平起见,法官选出的M人必须满足原告打分总和D和控方打分总和P的差的绝对值|D-P|最小。如果选择方法不唯一,再从中选出D+P的值最大的方案。
第一行有2个用空格隔开的整数n和m,便是候选人。
接下来的n行每行为用空格隔开的两个数,分别表示a[i]和b[i]。
输出包含两行,第一行为两个数D和P,见题意描述;第二行按照从小到大的顺序输出m个候选人的编号。
4 2 1 2 2 3 4 1 6 2
6 4 2 3
【数据范围】
100%的数据满足:1<=n<=200,1<=m<=20
我们很容易看出这个题是dp,但是需要用到各种小技巧。;
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define int long long using namespace std; int n,m,a[10050],b[10050],dp[1050][1050],path[1050][1050],stand,ans[1050]; signed main() { scanf("%lld%lld",&n,&m); for(int i=1;i<=n;i++)scanf("%lld%lld",&a[i],&b[i]); memset(dp,-1,sizeof dp);//标记所有状态均为不可行 stand=20*m; dp[0][stand]=0;//辩控差为0、选了0个人时最大辩控和为0 for(int j=0;j<m;j++)//枚举已经选了几个人 { for(int k=0;k<=stand*2;k++) { if(dp[j][k]>=0)//如果这种状态可行 { for(int i=1;i<=n;i++)//再选一个 { if(dp[j][k]+a[i]+b[i]>dp[j+1][k+a[i]-b[i]])//比较辩控和 { int t1=j,t2=k; while(t1>0&&path[t1][t2]!=i) { t2-=a[path[t1][t2]]-b[path[t1][t2]]; --t1; }//倒序查找i有没有被前j个人选过 if(!t1) { dp[j+1][k+a[i]-b[i]]=dp[j][k]+a[i]+b[i]; path[j+1][k+a[i]-b[i]]=i; }//如果没有选过,记录路径和辩控和 } } } } } int j=0; while(dp[m][stand+j]<0&&dp[m][stand-j]<0)++j;//双向查找直到找到一种方案可行 int k=(dp[m][stand+j]>dp[m][stand-j])?stand+j:stand-j;//比较绝对值一样时两种辩控和大小 printf("%lld %lld\n",(k-stand+dp[m][k])/2,(stand-k+dp[m][k])/2);//和差原理求D和P int t=k; for(int i=1;i<=m;i++) { ans[i]=path[m-i+1][t]; t-=a[path[m-i+1][t]]-b[path[m-i+1][t]]; }//逆序查找选过哪些人 sort(ans+1,ans+m+1);//按字典序排序 for(int i=1;i<=m;i++)printf("%lld ",ans[i]);//输出答案 return 0; }