链接
https://www.luogu.org/problemnew/show/P1080
大意
有 个人,除第一个人以外每个人能获得的奖赏即为左边所有人左手上的数的成绩除以自己右手上数字的商,试调整除第一人外的顺序,使收益最高的人收益最小
思路
可以发现,交换相邻
两个人的位置只对这两人的利益造成影响,而对其他人是没有任何影响的,所以我们只需比较交换两个人会形成怎样的变换
对于任意一种顺序,设 名大臣左右手上的数字分别是 和 ,国王为 和
如果我们交换两个相邻的大臣,交换前两人能获得的价值是
交换之后这两个大臣获得的奖励是
然后我们转换得到
我们提取公因式 后,实际只需要比较这两个式子的大小关系
使两边同时乘以 ,变成
因为大臣手里的数都是正整数所以一式的右边一定大于二式的左边,二式的右边一定大于一式的左边
,所以我们只需比较两式的右边,即比较
而这种相邻两数的比较方法正好可以运用在冒泡排序上,而冒泡排序的比较函数同样可以运用在其他排序上,所以我们一样可以通过快排来实现方法,其实在转换的过程中,我们已经证明了贪心的正确性,我们称这种证明方法为微扰
扫描二维码关注公众号,回复:
2768718 查看本文章
由于数据过大,需要高精度!
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;int n,add[20010],sum[20010],ans[20010];//这三个数组为中途运算的高精度数组
struct node
{
int a,b;
long long m;
}a[1001];
inline bool cmp(node x,node y){return x.m<y.m;}//贪心关键
inline void times(register int x)//乘法运算
{
memset(add,0,sizeof(add));
for(register int i=1;i<=ans[0];i++)
{
ans[i]*=x;
add[i+1]+=ans[i]/10;
ans[i]%=10;
}
for(register int i=1;i<=ans[0]+4;i++)//处理位数
{
ans[i]+=add[i];
ans[i+1]+=ans[i]/10;
ans[i]%=10;
if(ans[i]) ans[0]=max(ans[0],i);
}
return;
}
inline void divs(register int x)//除法
{
memset(add,0,sizeof(add));
int ys=0;
for(register int i=ans[0];i>0;i--)
{
ys=(ys<<3)+(ys<<1)+ans[i];
add[i]=ys/x;
if(!add[0]&&add[i]!=0) add[0]=i;
ys%=x;
}
return;
}
inline bool campore()//比较
{
if(add[0]!=sum[0]) return add[0]>sum[0];
for(register int i=max(add[0],sum[0]);i>0;i--)
if(add[i]!=sum[i]) return add[i]>sum[i];
}
inline void swap()//交换
{
memset(sum,0,sizeof(sum));
for(register int i=add[0];i>=0;i--) sum[i]=add[i];
return;
}
signed main()
{
scanf("%d",&n);
for(register int i=0;i<=n;i++) scanf("%d %d",&a[i].a,&a[i].b),a[i].m=a[i].a*a[i].b;
sort(a+1,a+1+n,cmp);
ans[0]=1;ans[1]=1;
for(register int i=1;i<=n;i++)
{
times(a[i-1].a);
divs(a[i].b);
if(campore()) swap();//找出大臣中获得利益最高的
}
for(register int i=sum[0];i>0;i--) putchar(sum[i]+48);//输出
}