【NOIP2012提高組】國王遊戲
Description
恰逢H國國慶,國王邀請n位大臣來玩一個有獎遊戲。首先,他讓每個大臣在左、右
手上面分別寫下一個整數,國王自己也在左、右手上各寫一個整數。然後,讓這n位大臣排
成一排,國王站在隊伍的最前面。排好隊後,所有的大臣都會獲得國王獎賞的若干金幣,每
位大臣獲得的金幣數分別是:排在該大臣前面的所有人的左手上的數的乘積除以他自己右
手上的數,然後向下取整得到的結果。
國王不希望某一個大臣獲得特別多的獎賞,所以他想請你幫他重新安排一下隊伍的順序,
使得獲得獎賞最多的大臣,所獲獎賞儘可能的少。注意,國王的位置始終在隊伍的最前面。
Solution
用什麼演算法
首先看到最大最小的,想到了二分。推著推著推不出來,╮(╯▽╰)╭,二分
不過仔細想了一想,要求一個序列,並且改一下位置答案可能就不同,那麼就是有優先順序……貪心!!!排序!!!
演算法如何解題
看到第一個肯定不變,那麼還與什麼可以確定。我們發現最後一個人的答案,是這個人在所有順序中的值最大的情況。有意思。進一步思考,所有人左手的乘積為Z,當他最後時,答案是
證明:設l1,r1與l2,r2交換位置,l1之前的l乘積為Z1,l1之後的乘積為Z2
沒換之前的答案:
換了之後的答案:
把兩個式子移項之後就是
因為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]);
}