现在的位置: 首页 > 综合 > 正文

【贪心】vijos P1745 巧克力

2017年10月11日 ⁄ 综合 ⁄ 共 2497字 ⁄ 字号 评论关闭

转载:http://hi.baidu.com/samroxas/item/da6159cb9bf42f28ee4665e1

描述 Description   

在某年某月某日,小D莫名其妙的得到一块超级大的魔法巧克力,于是他决定将这块巧克力切成若干块送给幼儿园的其他小朋友。这是一块n*m的矩形巧克力,所以小D准备将它切成n*m块。 
由于这块巧克力是一块魔法巧克力,所以必须按照特殊的方法进行切割。巧克力上共有n-1条横线和m-1条竖线,每次小D可以沿着其中的一条线切开某一块。而且这样切一次的代价只跟所切的线有关,而与所切的长度无关。沿着每条横线切一次的代价依次为y1,y2,……yn-1,竖线为x1,x2,……xm-1。 
例如,对于图中的巧克力,我们先沿着3条横线切,再沿5条竖线切,最终代价为y1+y2+y3+4*(x1+x2+x3+x4+x5)。 

由于小D想要代价最少,所以他向你求助。 

输入格式 Input Format  

文件第一行为一个整数T,代表数据组数。 
对于每个数据,第一行为两个整数n和m,意义如题目中所述。 
接下来n-1行,每行一个整数,分别代表x1,x2,……xn-1。 
接下来m-1行,每行一个整数,分别代表y1,y2,……ym-1。 

输出格式 Output Format  

对于每个数据输出一行,该行包含一个整数为最小代价。

样例输入

1

6 4

2

1

3

1

4

4

1

2

样例输出

42

哭了。。。本来基本的贪心思路是对的,每次先切最大的。结果一激动,每次取最大的时候比较成了变更之后的比较。。。其实就是每次且最初的代价最大的就好了。。。

贪心证明:简单的贪心。由于对于两条横线(或竖线),显然应当先切值大的会更优。对于一条横线和一条竖线,不妨设切横线的价值为x,设切竖线的价值为y,此时横线已切了A次,竖线已切了B次。若有x>y,则必有x*B+y*(A+1)<x*(B+1)+y*A。故对整体而言,按照从大到小的顺序切也会最优,所以将所有的值的排序后按照从大到小的顺序切过去就行。 该证明给出的是当本刀切X下刀切Y或者本刀切Y下刀切X的情况。但是,如果需要连着切呢?如X1,X2,X3都比Y1要大,我们把X1,X2,X3都切完再去切Y1这样是不是最优的呢?当然是,仍然是横切了A次,竖切了B次,

那么按照X1,X2,X3,Y1的顺序切的代价是:B*(X1+X2+X3)+(A+3)*Y1①

而按照其他顺序,如X1,Y1,X2,X3的顺序来切的代价为:B*X1+(B+1)*(X2+X3)+(A+1)*Y1②

用②式-①式可得:X2+X3-2Y1>0显然成立(因为X1,X2,X3均大于Y1)

Y1放在其它地方也同理可证X1,X2,X3,Y1这种顺序是最优的。X再多一点也一样,对于Y比较大的同理。所以贪心是正确的。

program vijos1745;
var
 pp,timex,timey,q,restx,resty,headx,heady,t,n,m,i:longint;
 y,x:array[1..10000] of longint;
 ans:int64;

procedure exc(var i,j:longint);
var
 k:longint;
begin
 k:=i;
 i:=j;
 j:=k;
end;

procedure sortx(i,j:longint);
var
 l,r,m:longint;
begin
 l:=i;
 r:=j;
 m:=x[(i+j) div 2];
 repeat
  while x[i]>m do
   inc(i);
  while x[j]<m do
   dec(j);
  if i<=j then
   begin
    exc(x[i],x[j]);
    inc(i);
    dec(j);
   end;
 until i>j;
 if i<r then
  sortx(i,r);
 if l<j then
  sortx(l,j);
end;

procedure sorty(i,j:longint);
var
 l,r,m:longint;
begin
 l:=i;
 r:=j;
 m:=y[(i+j) div 2];
 repeat
  while y[i]>m do
   inc(i);
  while y[j]<m do
   dec(j);
  if i<=j then
   begin
    exc(y[i],y[j]);
    inc(i);
    dec(j);
   end;
 until i>j;
 if i<r then
  sorty(i,r);
 if l<j then
  sorty(l,j);
end;

begin
 readln(t);
 for q:=1 to t do
  begin
   fillchar(x,sizeof(x),0);
   fillchar(y,sizeof(y),0);
   readln(n,m);
   ans:=0;
   for i:=1 to n-1 do
    readln(x[i]);
   for i:=1 to m-1 do
    readln(y[i]);
   if (n=1) and (m=1) then
    begin
     writeln(0);
     continue;
    end;
   if n=1 then
    begin
     for i:=1 to m-1 do
      ans:=ans+y[i];
     writeln(ans);
     continue;
    end;
   if m=1 then
    begin
     for i:=1 to n-1 do
      ans:=ans+x[i];
     writeln(ans);
     continue;
    end;
   sortx(1,n-1);
   sorty(1,m-1);//两个队,从大到小排序,每次比较两个队的队头,取较大的即可。

   timex:=0;
   timey:=0;
   headx:=1;
   heady:=1;
   restx:=n-1;
   resty:=m-1;
   while (restx>0) or (resty>0) do
    begin
     if x[headx]>y[heady] then
      begin
       ans:=ans+x[headx]+timex*x[headx];//timex与timey表示行和列需要多切多少刀。。。
       inc(timey);
       inc(headx);
       dec(restx);
      end
     else
      begin
       ans:=ans+y[heady]+timey*y[heady];
       inc(timex);
       inc(heady);
       dec(resty);
      end;
    end;
   writeln(ans);
  end;
end.

抱歉!评论已关闭.