国王游戏[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         ∴最优解选择大臣1在前面,当sum*a1/b2            最优解选择大臣2在前面,当sum*a2/b1 这样我们就得到贪心的策略了,即对于序列P,满足任意ai*bi
代码:
 #include
using namespace std;
#include
#include
#include
#include
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[1000];
int ans[10000],anstmp[10000],t[10000],tmpt[10000];
inline bool gtr(){
if(ans[0]
for(hd j=ans[0];j;--j)
if(ans[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
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
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;
}

你可能感兴趣的:(贪心)