贪心算法——皇后游戏(洛谷P2123)

贪心算法——皇后游戏

1、题目描述

题目背景
还记得 NOIP 2012 提高组 Day1 的国王游戏吗?时光飞逝,光阴荏苒,两年过去了。国王游戏早已过时,如今已被皇后游戏取代,请你来解决类似于国王游戏的另一个问题。

题目描述
皇后有 n 位大臣,每位大臣的左右手上面分别写上了一个正整数。恰逢国庆节来临,皇后决定为 n 位大臣颁发奖金,其中第 i 位大臣所获得的奖金数目为第i-1 位大臣所获得奖金数目与前 i 位大臣左手上的数的和的较大值再加上第 i 位大臣右手上的数。

形式化地讲:我们设第 i 位大臣左手上的正整数为 ai,右手上的正整数为 bi,则第 i 位大臣获得的奖金数目为 ci可以表达为:

当然,吝啬的皇后并不希望太多的奖金被发给大臣,所以她想请你来重新安排一下队伍的顺序,使得获得奖金最多的大臣,所获奖金数目尽可能的少。

注意:重新安排队伍并不意味着一定要打乱顺序,我们允许不改变任何一位大臣的位置。

输入格式
第一行包含一个正整数 T,表示测试数据的组数。

接下来 T 个部分,每个部分的第一行包含一个正整数 n,表示大臣的数目。

每个部分接下来 n 行中,每行两个正整数,分别为 ai和 bi,含义如上文所述。

输出格式
共 T 行,每行包含一个整数,表示获得奖金最多的大臣所获得的奖金数目。

输入输出样例
输入 #1 复制
1
3
4 1
2 2
1 2
输出 #1 复制
8
输入 #2 复制
2
5
85 100
95 99
76 87
60 97
79 85
12
9 68
18 45
52 61
39 83
63 67
45 99
52 54
82 100
23 54
99 94
63 100
52 68
输出 #2 复制
528
902

说明/提示
按照 1、2、3 这样排列队伍,获得最多奖金的大臣获得奖金的数目为 10;

按照 1、3、2 这样排列队伍,获得最多奖金的大臣获得奖金的数目为 9;

按照 2、1、3 这样排列队伍,获得最多奖金的大臣获得奖金的数目为 9;

按照 2、3、1 这样排列队伍,获得最多奖金的大臣获得奖金的数目为 8;

按照 3、1、2 这样排列队伍,获得最多奖金的大臣获得奖金的数目为 9;

按照 3、2、1 这样排列队伍,获得最多奖金的大臣获得奖金的数目为 8。

当按照 3、2、1 这样排列队伍时,三位大臣左右手的数分别为:

(1, 2)、(2, 2)、(4, 1)

第 1 位大臣获得的奖金为 1 + 2 = 3;

第 2 位大臣获得的奖金为 max{3, 3} + 2 = 5;

第 3 为大臣获得的奖金为 max{5, 7} + 1 = 8。

对于全部测试数据满足:T <= 10, 1 <= n <= 20000,1 < ai,bi < 10^9

2、问题分析

这题乍一看和P1080 国王的游戏很像啊,因此咱还是根据题意先用数学的思想建模推导一番:

设第i个大臣的收益为ci,左手金额为ai,右手金额为bi,第j个大臣的收益为cj,左手金额为aj,右手金额为bj,第i个大臣前i-1个人的金额之和为x,第i-1个大臣的收益为y。

                      左手金额       右手金额      
   前i-1个人              x            /
   第i个大臣              ai           bi
   第j个大臣              aj           bj

根据题目的要求,有:
ci = max{y,x+ai} + bi

cj = max{ci,x+ai+aj}+bj
   = max{max{y,x+ai} + bi,x+ai+aj}+bj
   = max{y+bi+bj,x+ai+bi+bj,x+ai+aj+bj}

此时两位大臣的中的较大收益记为C1, 显然

C1 = cj = max{y+bi+bj,x+ai+bi+bj,x+ai+aj+bj}

因为我们是要研究排列的方式对整体最大收益的影响,因而改变第i个大臣和第j个大臣的排列

                      左手金额       右手金额       
   前i-1个人              x            /
   第j个大臣              aj           bj
   第i个大臣              ai           bi

根据题目的要求,有:
cj = max{y,x+aj} + bj

ci = max{cj,x+ai+aj}+bi
   = max{max{y,x+aj} + bj,x+ai+aj}+bi
   = max{y+bi+bj,x+aj+bi+bj,x+ai+aj+bi}

此时两位大臣的中的较大收益记为C2, 显然

C2 = ci = max{y+bi+bj,x+aj+bi+bj,x+ai+aj+bi}

设C1为C1、C2中的较小者,则 C1 <= C2
max{y+bi+bj,x+ai+bi+bj,x+ai+aj+bj}
<= max{y+bi+bj,x+aj+bi+bj,x+ai+aj+bi}
=>
max{x+ai+bi+bj,x+ai+aj+bj}
<=max{x+aj+bi+bj,x+ai+aj+bi}
=>
max{ai+bi+bj,ai+aj+bj}<=max{aj+bi+bj,ai+aj+bi}
=>
max{bi,aj}+ai+bj<=max{bj,ai}+aj+bi
=>
max{bi,aj}-aj-bi <= max{bj,ai}-ai-bj
=>
-min{aj,bi}<=-min{ai,bj}
=>
min{ai,bj}<=min{aj,bi}

同理,设C2为C1、C2中的较小,可得到类似推论。

但这题与P1080国王的游戏不同的是,ai,bi,aj,bj之间没有明显的直接大小关系,而直接用式子min{ai,bj}<=min{aj,bi}进行排序,会容易出问题,所以我们需要分情况讨论ai,bi,aj,bj之间的大小关系。

考虑到直接进行分类判断情况过于冗杂,故引入变量d = a - b/|a-b|,表示每个大臣左右手金额大小关系,d的值有三种情况,
①、d = 1, 即a > b,
②、d = 0, 即a = b,
③、d = -1, 即a < b

此时情况可分为以下三种:
(1)、di < dj
(2)、di > dj
(3)、di = dj

对于情况(1):因为di<dj,所以ai-bi<aj-bj,设此时ai<aj,bi>bj(因为令ai<aj,bi>bj可使得ai-bi<aj-bj),为满足式子min{ai,bj}<=min{aj,bi},令ai<bi,bj<aj,即di=ai-bi<0,dj=aj-bj>0,满足di<dj,按di<dj排序即可。

对于情况(2):因为di>dj,所以ai-bi>aj-bj,设此时ai>aj,bi<bj,为满足式子min{ai,bj}<=min{aj,bi},令ai<bi,bj<aj,即di=ai-bi<0,dj=aj-bj>0,满足di<dj,按di<dj排序即可。

对于情况(3):因为di=dj,
①设di < 0,dj < 0,即ai < bi, aj < bj,为满足式子min{ai,bj}<=min{aj,bi},则ai<aj,按ai<aj排序即可。
②、设di = 0,dj = 0,即ai=bi,aj=bj,此时式子min{ai,bj}<=min{aj,bi},变成ai<aj,按ai<aj排序即可。
③、设di > 0, dj > 0,即ai > bi, aj > bj,为满足式子min{ai,bj}<=min{aj,bi},则bi>bj,按bi>bj排序即可。

3、算法源码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct hand{
	ll a;
	ll b;
	ll d;
} h[20001];
ll c[20001];
bool cmp(hand h1,hand h2){
	if(h1.d != h2.d) return h1.d < h2.d;
	if(h1.d <= 0) return h1.a < h2.a;
	return h1.b > h2.b;
}
int main(){
	int T;
	cin >> T;
	while(T--){
		int n;
		cin >> n;
		for(int i = 1; i <= n; i++)
		{
			cin >> h[i].a >> h[i].b;
			if(h[i].a > h[i].b) h[i].d = 1;
			else if(h[i].a == h[i].b) h[i].d = 0;
			else h[i].d = -1;
		}
		sort(h + 1, h + 1 + n, cmp);
		for(int i = 1; i <= n; i++){
			//因为前面已经排完序了,所以不需要用到单个a的值,所以可以用前缀和计算 
			h[i].a += h[i-1].a;
			c[i] = max(c[i-1],h[i].a)+ h[i].b;
		}
		cout << c[n] << endl;
	}
	
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值