P1080 [NOIP2012 提高组] 国王游戏题解

题目描述:

恰逢 H 国国庆,国王邀请 n n n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n n n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。

国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

大致思路:

妥妥的贪心题,我们把所有人的左手存进 a a a 数组,把所有人的左手存进 b b b 数组,因为直接看整体,很难做,那么我们可以从两个人中找出规律,再依次循环下去。现在有两个人,它们的下标分别为 x x x y y y,那么接下来就可以进行分类讨论:

  • 把下标为 x x x 的人放在前面,把下标为 y y y 的人放在后面。

  • 把下标为 y y y 的人放在前面,把下标为 x x x 的人放在后面。

接着,把前面人左手的乘积看做 s s s

  • 对于把下标为 x x x 的人放在前面,把下标为 y y y 的人放在后面,这种可能 x , y x,y x,y 的收益分别为 s ÷ b x s ÷ b_x s÷bx s × a x ÷ b y s × a_x ÷ b_y s×ax÷by

  • 对于把下标为 y y y 的人放在前面,把下标为 x x x 的人放在后面,这种可能 y , x y,x y,x 的收益分别为 s ÷ b y s ÷ b_y s÷by s × a y ÷ b x s × a_y ÷ b_x s×ay÷bx

对于 x x x y y y 之前的人来说不影响他们的钱数,对于 x x x y y y 后面的人来说也不影响他们的钱数,所以我们只要考虑 max ⁡ ( s ÷ b x , s × a x ÷ b y ) \max(s ÷ b_x,s × a_x ÷ b_y) max(s÷bx,s×ax÷by) max ⁡ ( s ÷ b y , s × a y ÷ b x ) \max(s ÷ b_y,s × a_y ÷ b_x) max(s÷by,s×ay÷bx) 的大小关系。

通过约分可以简化为 max ⁡ ( b y , a x × b x ) \max(b_y,a_x × b_x) max(by,ax×bx) max ⁡ ( b x , a y × b y ) \max(b_x,a_y × b_y) max(bx,ay×by),那么取出两个 max ⁡ \max max 中的值,假设 a x × b x < a y × b y a_x × b_x < a_y × b_y ax×bx<ay×by 有四种可能:

  • 左边取 b y b_y by,右边选 b x b_x bx,那么大小关系为 b y > a x × b x > b x > a y × b y b_y > a_x × b_x > b_x > a_y × b_y by>ax×bx>bx>ay×by,与假设结合矛盾,不成立。

  • 左边取 b y b_y by,右边选 a y × b y a_y × b_y ay×by,与假设结合成立,此时前者 ≤ ≤ 后者。

  • 左边取 a x × b x a_x × b_x ax×bx,右边选 b x b_x bx,那么大小关系为 a x × b x > b x > a y × b y a_x × b_x > b_x > a_y × b_y ax×bx>bx>ay×by,与假设结合矛盾,不成立。

  • 左边取 a x × b x a_x × b_x ax×bx,右边选 a y × b y a_y × b_y ay×by,与假设结合成立,此时前者 ≤ ≤ 后者。

我们发现只有两种可能是成立的,而且都是前者 ≤ ≤ 后者,说明把 x x x 放在 y y y 前面更优。

以此类推,最后别忘了加上高精度。

代码实现:

#include<bits/stdc++.h>
using namespace std;
struct node{
	int l,r,z;
}a[100001];
int n;
int c[100001],bs[100001],op[100001];
bool cmp(node n,node m){//贪心qwq
	return n.z<m.z;
}
void mul(int a[],int b){
	int t=0;
	for(int i=1;i<=a[0];i++){
		a[i]=a[i]*b+t;
		t=a[i]/10;
		a[i]%=10;
	}
	while(t>0){
		a[++a[0]]=t%10;
		t/=10;
	}
}

void add(int a[],int b){
	int t=0;
	memset(op,0,sizeof op);
	for(int i=a[0];i;i--){
		op[i]=(a[i]+10*t)/b;
		t*=10;
		t=t+a[i]-op[i]*b;
	}
	op[0]=a[0];
	while(!op[op[0]]) op[0]--;
}

bool maxx(int a[],int b[]){
	if(a[0]>b[0]) return 1;
	if(a[0]<b[0]) return 0;
	int i=a[0];
	for(i=a[0];a[i]==b[i]&& i;i--){
		
	}
	if(!i){
		return 0;
	}
	if(a[i]<b[i]) return 0;
	if(a[i]>b[i]) return 1;
}

signed main(){
	cin>>n;
	for(int i=0;i<=n;i++){
		cin>>a[i].l>>a[i].r;
		a[i].z=a[i].l*a[i].r;
	}
	if(n==1){
		cout<<a[0].l/a[1].r;
		return 0;
	}
	sort(a + 1,a + n + 1,cmp);
	c[0]=1;
	c[1]=a[0].l;
	memset(bs,0,sizeof bs);
	for(int i=1;i<=n;i++){
		mul(c,a[i].l);
		add(c,a[i].z);
		if(maxx(op,bs)) memcpy(bs,op,sizeof(bs));
	}
	for(int i=bs[0];i;i--){
		cout<<bs[i];
	}
	return 0;
}
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值