【NOIP2012】国王游戏(一类全序问题)

设按新安排的队伍顺序第 i i i 个人左手数字为 a i a_i ai,右手数字为 b i b_i bi

我们考虑什么时候交换第 i i i 个人和第 i + 1 i+1 i+1 个人不会更优。

设前 i − 1 i-1 i1 个人获得的金币的最大值为 p r e pre pre,他们 a i a_i ai 的乘积为 s s s

由于交换第 i i i 个人和第 i + 1 i+1 i+1 个人对后面的人获得的金币数没有影响,所以我们只需要让前 i + 1 i+1 i+1 个人的最大值最小。

交换前: a n s 1 = max ⁡ ( p r e , s b i , s ⋅ a i b i + 1 ) ans_1=\max\left(pre,\dfrac{s}{b_i},\dfrac{s\cdot a_i}{b_{i+1}}\right) ans1=max(pre,bis,bi+1sai)

交换后: a n s 2 = max ⁡ ( p r e , s b i + 1 , s ⋅ a i + 1 b i ) ans_2=\max\left(pre,\dfrac{s}{b_{i+1}},\dfrac{s\cdot a_{i+1}}{b_i}\right) ans2=max(pre,bi+1s,bisai+1)

交换第 i i i 个人和第 i + 1 i+1 i+1 个人不会更优当且仅当 a n s 1 ≤ a n s 2 ans_1\leq ans_2 ans1ans2

显然有 s b i ≤ s ⋅ a i + 1 b i \dfrac{s}{b_i}\leq \dfrac{s\cdot a_{i+1}}{b_i} bisbisai+1 s b i + 1 ≤ s ⋅ a i b i + 1 \dfrac{s}{b_{i+1}}\leq \dfrac{s\cdot a_i}{b_{i+1}} bi+1sbi+1sai,所以 a n s 1 ≤ a n s 2 ans_1\leq ans_2 ans1ans2 的充分条件是 s ⋅ a i b i + 1 ≤ s ⋅ a i + 1 b i \dfrac{s\cdot a_i}{b_{i+1}}\leq \dfrac{s\cdot a_{i+1}}{b_i} bi+1saibisai+1

得到 a i b i ≤ a i + 1 b i + 1 a_ib_i\leq a_{i+1}b_{i+1} aibiai+1bi+1,于是我们对所有人按 a i b i a_ib_i aibi 排序即可。

貌似这里只考虑了相邻的交换,那么这个排队顺序为什么一定是最优的呢?我们可以这么想:如果这个排队顺序中存在某个 a i b i > a i + 1 b i + 1 a_ib_i>a_{i+1}b_{i+1} aibi>ai+1bi+1,那么交换 i i i i + 1 i+1 i+1 肯定是不劣的。所以最优排队顺序中一定对于所有的 i i i 都满足 a i b i ≤ a i + 1 b i + 1 a_ib_i\leq a_{i+1}b_{i+1} aibiai+1bi+1

需要高精度。

#include<bits/stdc++.h>

#define N 1010

using namespace std;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

struct data
{
	int a,b;
}p[N];

bool operator < (data a,data b)
{
	return a.a*a.b<b.a*b.b;
}

struct BigInt
{
	int len,a[4010];
	BigInt(){len=0;memset(a,0,sizeof(a));};
	BigInt(int x)
	{
		len=0;
		while(x)
		{
			a[++len]=x%10;
			x/=10;
		}
	}
};

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

BigInt operator * (BigInt a,int b)
{
	for(int i=1;i<=a.len;i++) a.a[i]*=b;
	int i;
	for(i=1;i<=a.len||a.a[i];i++)
	{
		a.a[i+1]+=a.a[i]/10;
		a.a[i]%=10;
	}
	a.len=i;
	return a;
}

BigInt operator / (BigInt a,int b)
{
	int now=0;
	for(int i=a.len;i>=1;i--)
	{
		now=now*10+a.a[i];
		a.a[i]=now/b;
		now%=b;
	}
	while(a.len>1&&!a.a[a.len]) a.len--;
	return a;
}

void print(const BigInt &a)
{
	for(int i=a.len;i>=1;i--)
		putchar('0'+a.a[i]);
}

int n,a,b;

int main()
{
	n=read(),a=read(),b=read();
	for(int i=1;i<=n;i++)
		p[i].a=read(),p[i].b=read();
	sort(p+1,p+n+1);
	BigInt prod(a);
	BigInt maxn;
	for(int i=1;i<=n;i++)
	{
		maxn=max(maxn,prod/p[i].b);
		prod=prod*p[i].a;
	}
	print(maxn);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值