【noip2012】国王游戏

题意:

有n个大臣和一个国王左右手都写了数字 让大臣们站在国王的后面 每个大臣能获得前面所有人左手上数字乘积除以该大臣右手上的数字的金钱 问如何安排大臣顺序 使得获得金钱最多的大臣获得的金钱最少

题解:

看到最大值的最小值 最先想到的肯定是二分答案判断答案是否可行

我有一个思路(未能验证正确性) 从后往前确定某个位置上要放哪个大臣

求出所有大臣左手乘积*国王左手上的数 再将大臣按左手从大到小排序 找到第一个能放在该位置的大臣(获得金钱<=二分出的mid) 放下去再求下一个大臣

如果每个大臣都不能放 返回false 否则true

但是这个方法有个问题 因为它的答案可能达到10^4000 所以二分的复杂度约为O(log(10^4000)) 再乘以检查的O(n^2)

复杂度达到越O(10^10) 再加上不懂证明检查的正确性 故放弃之= =

下面来讲下正解

其实做起来特别简单 重在证明- - 表示半小时拍完1A

只要将每个大臣按左手值*右手值从小到大排序 一个个放下去 求答案就行了 注意数据很大要打高精

证明:

orz神ak的证明方法

用x[i]、y[i]表示左、右手上的数

我们证明相邻两人i、i+1 如果x[i]*y[i]>x[i+1]*y[i+1] 那么将他们交换答案不会更差

设i前所有人左手乘积为a 则交换前两大臣获得的钱分别为a/y[i]、a*x[i]/y[i+1]

而交换后钱分别为a/y[i+1]、a*x[i+1]/y[i]

显然 a/y[i+1]<=a*x[i]/y[i+1]

又因为x[i]*y[i]>x[i+1]*y[i+1]

所以a*x[i+1]/y[i]<a*x[i]/y[i+1]

固交换后两大臣的钱都不比交换前第一个大臣的多

代码:

 1 #include <cstdio>
 2 #include <algorithm>
 3 using std::sort;
 4 const int N=1001;
 5 struct info{
 6     int a[5000];
 7     void print(){
 8         if (!a[0]) puts("0");
 9         else for (int i=a[0];i;i--) printf("%d",a[i]);
10     }
11 }ans,sum,one,zero,save;
12 struct inim{
13     int x,y;
14     inim(const int a=0,const int b=0):
15         x(a),y(b){}
16 }im[N];
17 inline bool cmp(inim x,inim y){ return x.x*x.y<y.x*y.y; }
18 inline info operator *(info a,int b){
19     info res;
20     int x=0,l=a.a[0];
21     for (int i=1;i<=l;i++){
22         x+=a.a[i]*b;
23         res.a[i]=x%10;
24         x/=10;
25     }
26     while (x) res.a[++l]=x%10,x/=10;
27     res.a[0]=l;
28     return res;
29 }
30 inline info operator /(info a,int b){
31     info res;
32     int x=0,l=a.a[0];
33     for (int i=l;i;i--){
34         x=x*10+a.a[i];
35         res.a[i]=x/b;
36         x%=b;
37     }
38     while (!res.a[l] && l) --l;
39     res.a[0]=l;
40     return res;
41 }
42 inline bool operator >(info a,info b){
43     if (a.a[0]>b.a[0]) return 1;
44     if (a.a[0]<b.a[0]) return 0;
45     for (int i=a.a[0];i;i--){
46         if (a.a[i]>b.a[i]) return 1;
47         if (a.a[i]<b.a[i]) return 0;
48     }
49     return 0;
50 }
51 int n,xx,yy;
52 int main(){
53     freopen("game.in","r",stdin);
54     freopen("game.out","w",stdout);
55     scanf("%d",&n);
56     scanf("%d%d",&xx,&yy);
57     for (int i=1;i<=n;i++) scanf("%d%d",&im[i].x,&im[i].y);
58     sort(im+1,im+n+1,cmp);
59     zero.a[0]=0;
60     one.a[0]=one.a[1]=1;
61     sum=one*xx;
62     ans=zero;
63     for (int i=1;i<=n;i++){
64         save=sum/im[i].y;
65         if (save>ans) ans=save;
66         sum=sum*im[i].x;
67     }
68     ans.print();
69 }
View Code
原文地址:https://www.cnblogs.com/g-word/p/3371342.html