poj1015——Jury Compromise

本文介绍了一种通过动态规划解决辩方与控方分数匹配问题的算法,旨在从候选人中选出一组人员,使得辩方与控方总分之差最小,并在多个解中选取总分最大的组合。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:n(1~200)个候选人,辩方和控方分别对他们打分(0~20)分别为p、d,从其中选出m个人,要求——这m个人辩方总分和控方总和的差值最小,如果存在多种情况则选辩方、控方总和最大的。

输入:n,m,p[i],d[i]

输出:sum(p[i]),sum(d[i]),选择的m个人(按编号的升序)


分析:动态规划

   1、用到2个数组——dp[i][j]:表示取i个候选人,使其辩控差为j的所有方案中,辩控和最大的方案的辩控和

                    path[i][j]:记录所选定的候选人的编号

   2、由于辩控差可以为负数,而我们要保证dp[i][j]中j的非负性,所以设置一个修正值real_m=m*20,这样辩控差                    区间从[-400,400]变成[0,800]

   3、主体就是三重循环——i从1到m,j从0到2*real_m,k从1到n,找到满足条件的k就可以记录到path数组中去,         详情见代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct P
{
	int sum,sub;
};
P caipan[201];
int path[21][801],dp[21][801];

bool OK(int a,int b,int c)
{
	while(a>0&&path[a][b]!=c)
	{
		b-=caipan[path[a][b]].sub;
		a--;
	}
	if(a==0)return true;
	else return false;
}

int main()
{
	int n,m,i,j,k,cas=1,id[21];
	while(scanf("%d%d",&n,&m),n+m)
	{
		memset(caipan,0,sizeof(caipan));
		for(i=1;i<=n;i++)
		{
			int p,d;
			scanf("%d%d",&p,&d);
			caipan[i].sum=p+d;
			caipan[i].sub=p-d;
		}

		memset(dp,-1,sizeof(dp));
		memset(path,0,sizeof(path));
		int real_m=m*20;
		dp[0][real_m]=0;
		for(i=1;i<=m;i++)
			for(j=0;j<=2*real_m;j++)
				if(dp[i-1][j]>=0)
				{
					for(k=1;k<=n;k++)
						if(dp[i][j+caipan[k].sub]<dp[i-1][j]+caipan[k].sum&&OK(i-1,j,k))
						{
							dp[i][j+caipan[k].sub]=dp[i-1][j]+caipan[k].sum;
							path[i][j+caipan[k].sub]=k;
						}
				}

		for(k=0;k<=real_m;k++)
            if(dp[m][real_m-k]>=0||dp[m][real_m+k]>=0)break;
        int tmp=dp[m][real_m-k]>dp[m][real_m+k]?(real_m-k):(real_m+k);
        //printf("+++%d\n",tmp);
        int sum_p=(dp[m][tmp]+(tmp-real_m))/2;
        int sum_d=(dp[m][tmp]-(tmp-real_m))/2;
        for(i=m,j=0;i>0;i--)
        {
        	id[j]=path[i][tmp];
        	tmp-=caipan[path[i][tmp]].sub;
        	j++;
        }
        sort(id,id+m);
        
        printf("Jury #%d\n",cas++); 
		printf("Best jury has value %d for prosecution and value %d for defence:\n",sum_p,sum_d);
        for(i=0;i<m;i++)
        	printf(" %d",id[i]);
        printf("\n\n");
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值