PKU ACM 1015 Jury Compromise (DP)

题目链接:Jury Compromise

 

问题描述:

控辩双方选择陪审团成员。每个陪审团成员对控辩双方来说,都有一个数值表示合适程度(0 ~ 20)。现在要从n个待选陪审员中选出m位,要是这m个人对控辩双方合适程度的差最小。如果有差相同的情况,那么选择对控辩双方合适程度的和较大的那个组合。

 

主要思路:

1. 动态规划

2. 记录从 n 个人中选出 j 个,并且当前组合的控辩双方合适程度差为 k,且控辩双方合适程度和为 w,记录形式为:

                                                    f[j][k] = w

3. 如果已知 f[j][k] = w,并且有一个陪审员 i  尚未被选中

    那么就有 f[j+1][k+minus[i]] = w + plus[i]

    这里 minus[i] 和  plus[i]  表示第 i 个陪审员的控辩合适程度差和合适程度和。

4. 下面就是对路径的记录,这里用 path[j][k] = i记录 i j k 之间的连接关系。

 

个人体会:

感觉动态规划的方法,其实并不神秘。只不过是在暴力破解的过程中,记录下中间步骤的解,避免重复的计算。

 

源代码:

#include <iostream>
#include <algorithm> 
using namespace std;

int n, m;
int* minus;
int* plus;
int f[20+1][800+1];
int path[20+1][800+1];

bool available(int di, int dj, int dk)
{
	while(dj)
	{
		if(path[dj][dk] == di)
		{
			return false;
		}
		else
		{
			dk = dk - minus[path[dj][dk]];
			dj = dj - 1;
		}
	}
	return true;
}

void DP()
{
	memset(f, -1, sizeof(f));
	memset(path, 0, sizeof(path));
	f[0][0] = 0;

	for(int j = 0; j < m; j++)
	{
		for(int k = 0; k < 40 * m; k++)
		{
			if(f[j][k] != -1)
			{
				for(int i = 0; i < n; i++)
				{
					if(available(i, j, k))		//判断第 i 个陪审员是否已被选中
					{
						if(f[j+1][k+minus[i]] < f[j][k] + plus[i])
						{
							f[j+1][k+minus[i]] = f[j][k] + plus[i];
							path[j+1][k+minus[i]] = i;
						}
					}
				}
			}
		}
	}
}

void Print()
{
	int k = 20 * m;
	int result = -1;
	int step = 1;

	if(f[m][k] != -1)
	{
		result = k;
	}
	else
	{
		while(true)
		{
			if((f[m][k+step] != -1) || (f[m][k-step] != -1))
			{
				result = f[m][k+step] > f[m][k-step] ? (k+step) : (k-step);
				break;
			}
			step = step + 1;
		}
	}

	int j = m;
	int i = 0;
	k = result;
	int* resultpath = new int[m];
	while(j)
	{
		resultpath[i] = path[j][k];
		k = k - minus[path[j][k]];
		j = j - 1;
		i = i + 1;
	}

	int prosecution = 0, defence = 0;
	for(i = 0; i < m; i++)
	{
		prosecution += minus[resultpath[i]] - 20 + plus[resultpath[i]];
		defence += plus[resultpath[i]] - minus[resultpath[i]] + 20;
	}

	cout << "Best jury has value " << prosecution / 2 << " for prosecution and value " << defence / 2 << " for defence: " << endl;

	sort(resultpath, resultpath + m);

	for(i = 0; i < m; i++)
	{
		cout << resultpath[i] + 1 << " ";
	}
	cout << endl;
}

int main()
{
	int index = 0;
	int a, b;

	while(true)
	{
		cin >> n >> m;

		if(n == 0) break;

		index++;
		minus = new int[n];
		plus = new int[n];

		for(int i = 0; i < n; i++)
		{
			cin >> a >> b;
			minus[i] = a - b + 20;
			plus[i] = a + b;
		}

		DP();
		cout << "Jury #" << index << endl; 
		Print();
	}

	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值