【NOIP 2012】 国王游戏

Description

恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。 
国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序, 
使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

Input

第一行包含一个整数 n,表示大臣的人数。 
第二行包含两个整数 a 和 b,之间用一个空格隔开,分别表示国王左手和右手上的整数。 接下来 n 行,每行包含两个整数 a 和 b,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。

Output

输出只有一行,包含一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。

Sample Input

3 
1 1 
2 3 
7 4 
4 6 

Sample Output

2

Hint

【输入输出样例说明】 
按1、2、3号大臣这样排列队伍,获得奖赏最多的大臣所获得金币数为2; 
按1、3、2这样排列队伍,获得奖赏最多的大臣所获得金币数为2; 
按2、1、3这样排列队伍,获得奖赏最多的大臣所获得金币数为2; 
按2、3、1这样排列队伍,获得奖赏最多的大臣所获得金币数为9; 
按3、1、2这样排列队伍,获得奖赏最多的大臣所获得金币数为2; 
按3、2、1这样排列队伍,获得奖赏最多的大臣所获得金币数为9。 
因此,奖赏最多的大臣最少获得 2 个金币,答案输出 2。 

对于 20%的数据,有 1≤ n≤ 10,0 < a、b < 8; 
对于 40%的数据,有 1≤ n≤20,0 < a、b < 8; 
对于 60%的数据,有 1≤ n≤100; 对于 60%的数据,保证答案不超过 10^9; 
对于 100%的数据,有 1 ≤ n ≤1,000,0 < a、b < 10000。

【分析】

    看到这道题的使得获得奖赏最多的大臣,所获奖赏尽可能的少,我的第一想法是二分答案。但是又仔细想了想,答案不具有单调性,二分是行不通的。

    再看看这道题,我感觉可以用贪心来做。也就是说先用贪心确定一种最优的方案,再在这个排列队伍中求得答案。

    可以这样思考,相邻两个的人交换对于前面的人答案没影响(这不是废话么),而且对于后面的人答案也没有影响(这里很关键)。也就是说相邻两人的位置交换只会对这两个人产生影响。那么我们就先考虑这两个人。

        设这两个人分别为i和i+1。左手数字为a[i]和a[i+1],右手数字为b[i]和b[i+1]。两人的金币数为w[i]和w[i+1]。

        记P[i]=a[1]*a[2]*a[3]*...*a[i]

        可得:

            w[i]=P[i-1]/b[i];

            w[i+1]=P[i]/b[i+1];

        又P[i]=P[i-1]*a[i]

        那么 w[i+1]=P[i-1]*a[i]/b[i+1]=w[i]*a[i]*b[i]/b[i+1]

    不难看出,在这个相邻的二元组中,前面的数不受后面的影响,而后面的金币数决定于w[i],a[i],b[i]。

    推广到整个排队方案,前面的金币数和a[i],b[i]都会影响后面的答案。贪心原则便出来了:按a[i]*b[i]为关键字从小到大排序,相同的顺序无所谓。最后再扫一遍,算出答案即可。

    此题需要注意一点:乘除法都要写高精度,且答案有10000多位。

【代码】

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<iostream>
#include<algorithm>
using namespace std;
int N;
int a[1005],b[1005],ka,kb;
int ans[20000],t[20000],lena,lent,tt[20000],t2[20000],len;
void _qst_ab(int l,int r)
{
	int i=l,j=r,ma=a[(i+j)>>1],mb=b[(i+j)>>1];
	while(i<=j)
	{
		while(a[i]*b[i]<ma*mb) i++;
		while(a[j]*b[j]>ma*mb) j--;
		if(i<=j)
		{
			swap(a[i],a[j]);
			swap(b[i],b[j]);
			i++;j--;
		}
	}
	if(l<j) _qst_ab(l,j);
	if(i<r) _qst_ab(i,r);
}
void _init()
{
	scanf("%d%d%d",&N,&a[0],&b[0]);
	for(int i=1;i<=N;i++)
	    scanf("%d%d",&a[i],&b[i]);
}
void _get_t(int Left,int Right)
{
	for(int i=1;i<=lent;i++)
	{
		tt[i]+=t[i]*Left;
		tt[i+1]+=tt[i]/10;
		tt[i]%=10;
	}
	lent++;
	while(tt[lent]>=10)
	{
		tt[lent+1]=tt[lent]/10;
		tt[lent]%=10;
		lent++;
	}
	while(lent>1&&tt[lent]==0) lent--;
	len=lent;
	memcpy(t,tt,sizeof(tt));
	memset(tt,0,sizeof(tt));
	for(int i=1,j=len;i<=len;i++,j--)
	    t2[i]=t[j];
	int x=0,y=0;
	for(int i=1;i<=len;i++)
	{
		y=x*10+t2[i];
		tt[i]=y/Right;
		x=y%Right;
	}
	x=1;
	while(x<len&&tt[x]==0) x++;
	memset(t2,0,sizeof(t2));
	for(int i=1,j=x;j<=len;i++,j++)
	    t2[i]=tt[j];
	memcpy(tt,t2,sizeof(t2));
	len=len+1-x;
}
bool _cmp()
{
	if(len>lena) return true;
	if(len<lena) return false;
	for(int i=1;i<=len;i++)
	{
	    if(ans[i]<tt[i]) return true;
	    if(ans[i]>tt[i]) return false;
	}
	return false;
} 
void _solve()
{
	_qst_ab(1,N);
	t[1]=1;lent=1;
	for(int i=1;i<=N;i++)
	{
		memset(tt,0,sizeof(tt));
		len=0;
		_get_t(a[i-1],b[i]);
		if(_cmp())
		{
		    memcpy(ans,tt,sizeof(tt));
		    lena=len;
		}
	}
	for(int i=1;i<=lena;i++)
	    printf("%d",ans[i]);
	printf("\n");
}
int main()
{
	_init();
	_solve();
	return 0;
}



评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值