【NOIP2012提高组】国王游戏

42 篇文章 0 订阅

##Description
恰逢H国国庆,国王邀请n位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右

手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这n位大臣排

成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每

位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右

手上的数,然后向下取整得到的结果。

国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,

使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。
##Solution
####用什么算法
首先看到最大最小的,想到了二分。推着推着推不出来,╮(╯▽╰)╭,二分
不过仔细想了一想,要求一个序列,并且改一下位置答案可能就不同,那么就是有优先级…贪心!!!排序!!!
####算法如何解题
看到第一个肯定不变,那么还与什么可以确定。我们发现最后一个人的答案,是这个人在所有顺序中的值最大的情况。有意思。进一步思考,所有人左手的乘积为Z,当他最后时,答案是 Z l [ i ] ∗ r [ i ] Z\over {l[i]*r[i]} l[i]r[i]Z,好像 l [ i ] ∗ r [ i ] l[i]*r[i] l[i]r[i]越大,那么这个人的贡献就越小,而且在其他排序下不会有其他位置的贡献总贡献比当前更小的了。因为如果选的是其他人在最后一位,那么假设就交换一下位置,那么两个位置的答案总和更大。
证明:设l1,r1与l2,r2交换位置,l1之前的l乘积为Z1,l1之后的乘积为Z2
没换之前的答案: Z 1 l 1 r 1 + Z 1 Z 2 l 2 r 2 {Z1\over{l1r1}}+{Z1Z2\over{l2r2}} l1r1Z1+l2r2Z1Z2
换了之后的答案: Z 1 l 2 r 2 + Z 1 Z 2 l 1 r 1 {Z1\over{l2r2}}+{Z1Z2\over{l1r1}} l2r2Z1+l1r1Z1Z2
把两个式子移项之后就是(下面问号表示不确定关系)
Z 1 ( Z 2 − 1 ) l 2 r 2 ? Z 1 ( Z 2 − 1 ) l 1 r 1 {Z1(Z2-1)\over{l2r2}}?{Z1(Z2-1)\over{l1r1}} l2r2Z1(Z21)?l1r1Z1(Z21)
Z 1 ( Z 2 − 1 ) l 1 r 1 ? Z 1 ( Z 2 − 1 ) l 2 r 2 Z1(Z2-1)l1r1?Z1(Z2-1)l2r2 Z1(Z21)l1r1Z1(Z21)l2r2
因为l1r1< l2r2,所以前项<后项(这里就是确定了问号的关系)
为什么考虑二元组的和呢?因为交换位置之后一个变大,一个变小,那么比较一下和再很据l1r1< l2r2之类的就可以分别出这样更优了。
根据上面的证明,按照l*r从小到大排一次续然后依次模拟就可以了。
####记得要打高进度
可惜,考场没打高进度
##Code

#include<iostream> 
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define ll long long
const int maxn=5007;
using namespace std;
ll i,j,k,n,m,yi,er,r,mid,l,t;
int ans[maxn],ans1[maxn],b[maxn];
char s[maxn],st[maxn];
struct node{
    ll a,b,c;
}a[maxn];
bool cmp(node x,node y){
    return x.c<y.c;
}
void cheng(ll x){
    memset(b,0,sizeof(b));
    ll i,j,k,l,o=0;
    fo(i,1,ans1[0]){
	    b[i]=b[i]+ans1[i]*x+o;
	    b[i+1]+=b[i]/10;
	    b[i]=b[i]%10;
	}
	for(b[0]=ans1[0];b[b[0]+1];){
	    b[++b[0]+1]+=b[b[0]]/10;
	    b[b[0]]=b[b[0]]%10;
	}
	fo(i,0,maxn) ans1[i]=b[i];
}
void chu(ll x){
    ll i,j,k,o=0;
    fod(i,1,b[0])b[i]=0;
	b[0]=0;
	memset(b,0,sizeof(b));
    fod(i,ans1[0],1){
	    o=o*10+ans1[i];
		if(o>=x){
		    if(b[0]==0) b[0]=i;
			b[i]=o/x;
			o=o%x;    
		}    
	}
}
void bijiao(){
	ll i,j,k,l;
	if(b[0]>ans[0]){
		fo(i,0,maxn) ans[i]=b[i]; 
	}
	else if(b[0]==ans[0]){
		fod(i,ans[0],1){
			if(b[i]>ans[i]){
				fo(j,0,maxn) ans[j]=b[j];    
				return;
			}
		
		}
	} 
}
int main(){
    scanf("%d",&n);
    scanf("%s",s+1);
    fod(i,strlen(s+1),1)ans1[++ans1[0]]=s[i]-'0';
    scanf("%d",&er);
    t=yi;
    fo(i,1,n){
        scanf("%lld%lld",&a[i].a,&a[i].b);
        a[i].c=a[i].a*a[i].b;
    }
    sort(a+1,a+n+1,cmp);
    fo(i,1,n){
        chu(a[i].b);
		bijiao();
		cheng(a[i].a); 
		if(i==95){
		    n=n;
		}  
    }
    fod(i,ans[0],1)printf("%d",ans[i]);
}
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值