[NOIP2012]国王游戏 题解
题目大意:
n个人排成一排,排头固定,其他可以变。每一个人左右手都有一个整数,一个人的分数为他所有前面的人左手上的数的乘积除以他右手上的数(向下取整),求在整列中最大分数的最小值。
思路:
首先,一切序列都可以通过若干次相邻的人的交换实现转换,而相邻的人的交换只会影响这两个人的分数。
假设相邻的两人为i,i+1,则令a[i]*b[i]<=a[i+1]*b[i+1],设i之前的和为S,则交换前的ans1=max{S/b[i],S*a[i]/b[i+1]},交换后ans2=max{S/b[i+1],S*a[i+1]/b[i]}。∵a[i],a[i+1],b[i],b[i+1],S均为正整数,∴S*a[i]>=S,∴S*a[i]/b[i+1]>=S/b[i+1]。
同理:S*a[i+1]/b[i]>=S/b[i]。
又∵a[i]*b[i]<=a[i+1]*b[i+1]且a[i],a[i+1],b[i],b[i+1],S均为正整数,∴S*a[i]*b[i]<=S*a[i+1]*b[i+1],∴S*a[i]/b[i+1]<=S*a[i+1]/b[i],∴ans2>=ans1,∴要使最终ans最小则要使每个人左右手的数的乘积从小到大排列,在计算答案。
由于数据较大,需要用到高精度(压位),乘法没什么问题,除法注意边界条件!
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int M=10005,N=100000; 6 int n,i,l,p,len,a[M],b[M],c[M],s[M],d[M],z[M],id[M],ans[M]; 7 8 bool cmp(int x,int y) { return s[x]<s[y]; } 9 10 void cheng(int x) 11 { 12 int i,t=0; 13 for (i=1;i<=len;++i) 14 { 15 c[i]=t+c[i]*x; 16 t=c[i]/N; 17 c[i]=c[i]%N; 18 } 19 if (t) c[++len]=t; 20 } 21 22 bool pd() 23 { 24 if (l>p) return 1; 25 for (int i=1;i<=l;i++) 26 if (ans[i]<d[i]) return 1; 27 return 0; 28 } 29 30 void chu(int y) 31 { 32 int i=len,t,x=z[len]; 33 for (l=0;i;) 34 { 35 if (i>1) { if (x<y) x=x*N+z[--i]; } else break; 36 d[++l]=x/y;x=x%y; 37 } 38 if (x>=y) d[++l]=x/y; 39 if (pd()) for (p=l,i=1;i<=l;i++) ans[i]=d[i]; 40 } 41 42 int main() 43 { 44 scanf("%d%d%d",&n,&a[0],&b[0]); 45 for (i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]),s[i]=a[i]*b[i],id[i]=i; 46 sort(id+1,id+1+n,cmp); c[len=1]=a[0]; 47 for (i=1;i<=n;i++) memcpy(z,c,sizeof c),chu(b[id[i]]),cheng(a[id[i]]); 48 printf("%d",ans[1]); 49 for (i=2;i<=p;i++)//printf("%05d",ans[i]); 50 if (ans[i]>10000) printf("%d",ans[i]); 51 else if (ans[i]>1000) printf("0%d",ans[i]); 52 else if (ans[i]>100) printf("00%d",ans[i]); 53 else if (ans[i]>10) printf("000%d",ans[i]); 54 else printf("0000%d",ans[i]); 55 return 0; 56 }
我一直在繁华的苍凉中徘徊着,用一颗OI的心寻找着生命和宇宙的美妙与玄奥。