[NOIP提升组]2012年 Day1-T2 国王游戏

题目ios

题目大意

让n 位大臣排成一排,国王站在队伍的最前面。排好队后,全部的大臣都会得到国王奖赏的若干金币,每位大臣得到的金币数分别是:排在该大臣前面的全部人的左手上的数的乘积除以他本身右手上的数,而后向下取整获得的结果。
大臣的位置能够从新排列
求从新排列后的队伍中获奖赏最多的大臣所得到的金币数最小web

题目解析

因为相邻的两个大臣交换位置并不影响前面和后面的值
因此这里假设svg

左手 右手
国王 x0 y0
大臣甲 x1 y1
大臣乙 x2 y2

由于国王位置固定不变,因此spa

当大臣甲在大臣乙的前面时,k1=max(x0/y1,x0*x1/y2)code

当大臣乙在大臣甲的前面时,k2=max(x0/y2,x0*x2/y1)xml

ans=min(k1,k2)即为所求排序

ip

n1=x0/y1;n2=x0*x1/y2ci

n3=x0/y2;n4=x0*x2/y1get

不妨设大臣甲在前为最后答案,而后推出关系式.

由于X!=0,因此n4>n1;n2>n3

由于n2<n4

即a0* a1/b2<a0 *a2/b1,

两边同乘以b1,b2再除以a0,可得

a1* b1<a2 *b2,所以排序按左右手乘积进行升序.

再看到数据规模a,b<=10000,用高精,有点麻烦,很少解释了

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#define N 10000
using namespace std;
int n,l,r,t,c,v;
int p[N],ans[N],k[N];
struct A 
{
	int l,r,p;
}a[1005];
bool cmp(A a,A b)
{
	return a.p<b.p;
}//按照左右手乘积升序 
void div()
{
	memcpy(k,p,sizeof(p));
	int b=0;
	while(k[b]==0&&b<N) b++;
	t=0;
	for(int i=b;i<N;i++)
	{
	  k[i]+=t;
	  t=k[i]%v*10;
	  k[i]/=v;
	}
}//高精除以单精 
void Max()
{
	div();
	int ki=0,ansi=0;
	while(k[ki]==0&&ki<N) ki++;
	while(ans[ansi]==0&&ansi<N) ansi++;
	if(ansi<ki)
	 return;
	else if(ansi>ki)
	 memcpy(ans,k,sizeof(k));
	else if(ansi==ki)
	{
	  for(int i=ansi;i<N;i++)
	  {
	  	if(ans[i]>k[i])
	  	 return;
	  	else if(ans[i]<k[i])
	  	{
		  memcpy(ans,k,sizeof(k));
		  return;
		}
	  }
	}
	return;
}//高精比较 
void Multiply()
{
	t=0;
	for(int i=N-1;i>=0;i--)
	{
	  p[i]=p[i]*c+t;
	  t=p[i]/10;
	  p[i]%=10;
	}
	return;
}
int main()
{
	cin>>n>>l>>r;
	for(int i=1;i<=n;i++)
	{
	  cin>>a[i].l>>a[i].r;
	  a[i].p=a[i].l*a[i].r;
	}//求每位大臣的左右手乘积 
	sort(a+1,a+1+n,cmp);//排序 
	p[N-1]=l;
	t=0;
	for(int i=N-1;i>=0;i--)
	{
	  p[i]+=t;
	  t=p[i]/10;
	  p[i]%=10;
	}
	for(int i=1;i<=n;i++)
	{
	  c=a[i].l;v=a[i].r;
	  Max();
	  Multiply();
	}
	int t=0;
	while(ans[t]==0&&t<N) t++;
	for(int i=t;i<N;i++)
	 cout<<ans[i];//输出 
	return 0;
}