国王游戏[NOIP2012]解题报告

  在做这道题之前已经预先知道这道题是贪心了,但是贪心的思路却一直没想到,所以看了题解,发现做法还是很神奇的
Step 1
一个定理
       对于一个序列,通过交换其相邻的两个元素,一定可以变成其全排列中的任意一个序列。
演绎证明:一个简单的想法是我们可以发现冒泡排序就是这么做的,那么上述定理的正确性不高于冒泡排序的正确性。
                 一个类似的稍微严谨的想法是,冒泡排序中每次交换等价于消除了一个逆序对,即任何一个序列必然可以通过消除逆序对变成字典序最小的排列,那么我们只需要将其过程相逆就可以得到原序列了,那么就等价于任何一个序列都可以通过不断的交换两个相邻元素得到其任一排列。
推论: 讨论相邻两个元素的交换是等价于改变序列的。
Step 2
        对于这道题的话,宏哥发现了一个很神的东西,那就是题解的第一步了:
                若在一个序列中交换两个相邻元素,其影响仅存在于其相邻元素中。
那么就讨论两个相邻元素的交换好了,既然讨论改变序列是困难的。
Step 3
        设七元组(P,a1,b1,a2,b2,sum,ans),(a1,b1)∈P,(a2,b2)∈P,sum是大臣1和大臣2前所有人左手上数字的乘积,ans是大臣1与大臣2所发钱财的最大值。
        若选择大臣1在前面,则ans=max(sum/b1,sum*a1/b2);
        若选择大臣2在前面,则ans=max(sum/b2,sum*a2/b1).
        
        易知,sum/b1<sum*a2/b1,且sum/b2<sum*a1/b2。
        ∴最优解选择大臣1在前面,当sum*a1/b2<sum*a2/b1,即a1*b1<a2*b2.
           最优解选择大臣2在前面,当sum*a2/b1<sum*a1/b2,即a2*b2<a1*b1
这样我们就得到贪心的策略了,即对于序列P,满足任意ai*bi<ai+1*bi+1,i∈[1,|P|]∩N,则其一定是最优序列之一。

代码:
 #include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
const int M=100000;
typedef short hd;
char * ptr=new char[M];
inline void in(hd &x){
while(*ptr<'0'||*ptr>'9')++ptr;
x=0;
while(*ptr>47&&*ptr<58)x=x*10+*ptr++-'0';
}
struct S{
hd l,r;
int ji;
inline bool operator < (S a) const {
return ji<a.ji;
}
}a[1000];
int ans[10000],anstmp[10000],t[10000],tmpt[10000];
inline bool gtr(){
if(ans[0]<anstmp[0])return 1;
for(hd j=ans[0];j;--j)
if(ans[j]<anstmp[j])
return 1;
else
return 0;
return 0;
}
inline void bigout(int * a){
printf("%d",a[a[0]]);
for(hd j=a[0];--j>0;)printf("%05d",a[j]);
printf("\n");
}
int main(){
freopen("kinggame.in","r",stdin);freopen("kinggame.out","w",stdout);
hd n,l,r,i,j;
fread(ptr,1,M,stdin);
in(n);
in(l);
in(r);
for(i=0;i<n;++i){
in(a[i].l);
in(a[i].r);
a[i].ji=a[i].l*a[i].r;
}
sort(a,a+n);
t[0]=1;
t[1]=l;
ans[0]=1;
ans[1]=0;
for(i=0;i<n;++i){
for(j=0;j<=t[0];++j)
tmpt[j]=t[j];
for(j=t[0];j;--j){
tmpt[j]+=tmpt[j+1]*M;
anstmp[j]=tmpt[j]/a[i].r;
tmpt[j]%=a[i].r;
}
anstmp[0]=t[0];
if(!anstmp[anstmp[0]])--anstmp[0];
if(gtr())
for(j=0;j<=anstmp[0];++j)
ans[j]=anstmp[j];
t[1]*=a[i].l;
for(j=2,++t[0];j<=t[0];++j){
t[j]=t[j]*a[i].l+t[j-1]/M;
t[j-1]%=M;
}
if(!t[t[0]])--t[0];
}
bigout(ans);
return 0;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值