NOIP2012 国王游戏 题解

描述

恰逢H国国庆,国王邀请n位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这n位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。 
国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

格式

输入格式

第一行包含一个整数n,表示大臣的人数。 
第二行包含两个整数a和b,之间用一个空格隔开,分别表示国王左手和右手上的整数。接下来n行,每行包含两个整数a和b,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。

输出格式

输出只有一行,包含一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。

样例1

样例输入1[复制]

3 
1 1 
2 3 
7 4 
4 6 

样例输出1[复制]

2

限制

每个测试点1s

提示

对于20%的数据,有1≤ n≤ 10,0 < a、b < 8; 
对于40%的数据,有1≤ n≤20,0 < a、b < 8; 
对于60%的数据,有1≤ n≤100; 
对于60%的数据,保证答案不超过10^9; 
对于100%的数据,有1 ≤ n ≤1,000,0 < a、b < 10000。

贪心证明

假设相邻的两个人左右手分别是(a, b), (A, B)。设a * b <= A * B,i之前所有人的左手乘积为S。

则,ans1 = max{S / b, S * a / B}
若交换
则,ans2 = max{S / B, S * A / b}
因为,a * b <= A * B
所以,S * a / B <= S * A / b
又因为,S / B <= S * a / B
所以,ans2 = S * A / b
ans1 = max{S / B[i], S * a / B}

所以,ans1 <= ans2

证毕

解一:

首先我们分析一下如果ij两个相邻那么i排在j前面的必要条件是  total *a[i] / b[j]  <  total * a[j] /b[i] 也就是说 a[i]*b[i]

而本题中,n1000ab10000  100010000乘起来。。。。

所以要用高精度,而高精度是以字符读入,那前面的排序要用一遍,麻烦。。。

所以本段程序有了改进:平时的精度数组只存一个一位数,这段程序存了每个大臣的数,每一次乘完都更新一遍,这样以后就可以读入时就处理好先后顺序,程序还很短。

【程序】

var i,n,l:longint;  //l表示高精度中字符串的长度

   a,b,c:array[0..100001] of longint; //a表示乘积,b表示左手,c表示右手

   g:array[0..1000000] of longint;

procedure gj1;   //高精乘

 varj:longint;

begin

 forj:=1 to l do g[j]:=g[j]*b[i];  //每一个都乘

 forj:=1 to l do

 begin

  g[j+1]:=g[j+1]+g[j] div 10;  //进位

  g[j]:=g[j] mod 10;  //进位后处理

 end;

 inc(l); //长度加一

 while g[l]>9 do  

 begin

  g[l+1]:=g[l+1]+g[l] div 10;  //单个位上》9 进位

  g[l]:=g[l] mod 10;  //进位后处理

  inc(l);  //长度加一

 end;

  ifg[l]=0 then dec(l);  //数组末位(数的首位)为0,出数组

 end;

procedure gj2;   //除法

var j:longint;

 begin

  forj:=l downto 1 do

  begin

   g[j-1]:=g[j-1]+(g[j] mod c[n])*10; //将前一位mod 第n位大臣右手给下一位(对于数来说)

   g[j]:=g[j] div c[n];   //处理这一位

  end;

 while g[l]=0 do dec(l);   //处理首位

  ifl=0 then writeln('1');  //防止减完

 end;

procedure qsort(l,r:longint); //快排

 vari,j,x,y:longint;

 begin

  i:=l;

  j:=r;

  x:=a[(l+r) div 2];

  repeat

   while a[i]

   while x

   if i<=j then

    begin

     y:=a[i]; a[i]:=a[j]; a[j]:=y;

     y:=b[i]; b[i]:=b[j]; b[j]:=y;   //每个人对应的三个数组都要跟着换

     y:=c[i]; c[i]:=c[j]; c[j]:=y;

     inc(i); dec(j);

    end;

  until (i>j);

   ifl

   ifi

 end;

begin

 readln(n);

 readln(b[0],c[0]);  //读入国王,国王不参与排序,所以‘0’

  fori:=1 to n do    //读入大臣,并计算左手*右手

  begin

   read(b[i],c[i]);

   a[i]:=b[i]*c[i];

  end;

 qsort(1,n); //排序,原理见题解

 l:=1;

 g[1]:=b[0];  //从国王左手开始乘,赋初值

  fori:=1 to n-1 do gj1;      gj2;     //最后一个得到的最多,所以由n-1个左手乘积/第n个右手乘积

  fori:=l downto 1 do write(g[i]);  //倒序输出

  writeln;

end.


代码二:

var
  i,j,k,m,n,x,y,len:longint;
  s,sa,sb:ansistring;
  a,ans,c:array[1..10000]of longint;
  b:array[1..1010,1..3]of longint;

procedure qs(head,tail:longint);
var t,mid,i,j:longint;
begin
   i:=head;j:=tail;mid:=b[(head+tail)>>1,3];
   repeat
     while b[i,3]
     while b[j,3]>mid do dec(j);
     if i<=j then
        begin
           t:=b[i,3];
           b[i,3]:=b[j,3];
           b[j,3]:=t;
           t:=b[i,1];
           b[i,1]:=b[j,1];
           b[j,1]:=t;
           t:=b[i,2];
           b[i,2]:=b[j,2];
           b[j,2]:=t;
           inc(i);dec(j);
        end;
    until i>j;
   if head
   if i
end;

procedure cheng(x:longint);
var i,j,k:longint;
begin
  for i:=1 to 10000 do
      a[i]:=a[i]*x;
  for i:=1 to 9999 do
      begin
         a[i+1]:=a[i+1]+a[i] div 10;
         a[i]:=a[i] mod 10;
      end;
end;

function bijiao:boolean;
var i,j:longint;
begin
  for i:=10000 downto 1 do
     begin
       if ans[i]>c[i] then exit(false);
       if c[i]>ans[i] then exit(true);
     end;
  exit(false);
end;
procedure dv(x:longint);
var i,d:longint;
begin
   d:=0;
   for i:=10000 downto 1 do
     begin
       d:=d*10+a[i];
       c[i]:=d div x;
       d:=d mod x;
     end;

end;

begin

   readln(n);
   readln(x,y);a[1]:=x;
   for i:=1 to n do
      begin
         readln(b[i,1],b[i,2]);
         b[i,3]:=b[i,1]*b[i,2];
      end;
   qs(1,n);
   len:=10000;
   for i:=1 to n do
      begin
        

你可能感兴趣的:(NOIP解题报告)