解题报告 比赛

 

1.        题目

比赛(Pk.pas/c/cpp

 

题目描述

     新兵们一般在部队没什么娱乐活动,而且还老有个警官在旁边即即歪歪,所以他们总要找个法子发一下怒气,所以有时候两个不同营的新兵就喜欢一起打架。。。。。。 A营和B营就是典型的例子。而且他们破坏力很大,常常把操场打得这里坑一块那里坑一块,警官很头疼,因为都要他掏腰包。。。。。。

     为了减少损失,他决定每隔一段时间由他自己组织搞次比赛(打架)。

     比赛开始时两个营新兵排成两个队,每个士兵有一个破坏值,然后由警官来安排比赛的人,分任意次来PK,可以是单挑,群挑,或者是单挑群(呵呵,警官只在乎修整费用最少)具体规则如下:

从后往前,每次从A营选出连续k1(k1>0)个人,从B营选出连续k2(k2>0)个人进行pk。那么这次破坏导致操场的修整费用为 (S1-K1)*(S2-K2)。比赛直到两个队伍都为空时结束。而这次比赛的总修整费用就为每次PK修整费用的和。

警官拜托你的是使这次比赛的费用最小。

    

输入数据

    第一行两个正整数L1,L2分别表示两营的人数;

第二行L1个正整数,描述A 营每个人的破坏值

第三行L2个正整数,描述B 营每个人的破坏值

     1<=L1,L2<=2000

输出数据

一个正整数表示操场的最小修整费用

 

 

样例输入

3 2

1 2 3

1 2

 

样例输出

2

 

2.        题目实质

动规求最优解。(废话)

3.        算法

地球人都能看出来是动规,问题是,方程怎么写?

第一种:

f[i,j] 代表消去序列A1i->l1,序列A2j->l2

A1[i]+A1[i+1]+……+A1[l1]=Si;A2[j]+A2[j+1]+……+A2[l2]=Tj ;则状态转移方程为: f[i,j]:=min{ f[p,q]+(Si-Sp-p+i)*(Tj-Tq-q+j)  |  (i<p<=l1+1,j<q<=l2+1) }

初始条件为 f[l1+1,l2+1]=0 ;

但这样是 O(L^4)

观察(S1-K1)*(S2-K2)(S1-K1)可以写成[(A1[1]-1)+(A1[2]-1)+……+(A1[K1]-1)],(S2-K2) 类似。

所以我们可以在读入时就把A1 A2 的所有数全部减 1 ,计算时直接 S1*S2

假设 S1*S2 S1 A1 序列中两个或两个以上元素的和, S2 类似。那么我们可以把 S1 拆成两个和 S11 S12 ;把 S2 拆成两个和 S21 S22 。则

S1*S2=(S11+S12)*(S21+S22)=S11*S21+S12*S21+S11*S22+S12*S22>S11*S21+S12*S22 ;

  可见把它们分开消去更优。所以要这样一直拆分下去,直到某个和 S 为一个元素的和。所以每次都在 A1A2 中选择某一序列的一个元素跟另外一个序列的一段(或一个)消去。

那么状态转移方程就变成了:

       f[i,j]:=min{  f[i+1,q]+A1[i]*(Tj-Tq)  |  (j<q<=l2+1)

                   f[p,j+1]+(Si-Sp)*A2[j]  |  (i<p<=l1+1)  }

 这样为 O(L^3)

假设A1[i]A2序列的一段 x->(y-1) 一起消去。如下图,

   A1[i]:                    

                      i

   A2[x]->A2[(y-1)]:       ……

                      x    x+1 x+2 ……  y-2 y-1

这个表示为 f[i,x]=f[i+1y]+A1[i]*(Tx-Ty)

显然可以看做是 A1[i] A2[x+1]->A2[y-1] 一起消去然后A1[i]再跟A2[x]一起消去,而前者为 f[i,x+1] , 后者为 A1[i]*A2[x]

  所以还可以表示为 f[i,x]=f[i,x+1]+A1[i]*A2[x];

那么状态转移方程就变成了:

       f[i,j]:=min{ f[i+1,j]+A1[i]*A2[j]         //A1序列的iA2序列从j开始的一段一起消去   

                  f[i,j+1]+A1[i]*A2[j]         //A1序列从i开始的一段与A2序列的j一起消去

                  f[i+1,j+1]+A1[i]*A2[j] }     //A1序列的iA2序列的j一起消去

这样就是O(L^2)的了。

 

4.        注意事项

注意 f 数组的初值!

5.        程序代码

SueMiller Pascal

var l1,l2:integer;

    a1,a2:array[0..2010]of longint;

    i,j:integer;

    f:array[0..2010,0..2010]of longint;

 

function min(a,b:longint):longint;

begin

  if a<b then exit(a) else exit(b);

end;

 

begin

  assign(input,'Pk.in');reset(input);

  assign(output,'Pk.out');rewrite(output);

 

  readln(l1,l2);

  for i:=1 to l1 do begin read(a1[i]);dec(a1[i]);end;

  for i:=1 to l2 do begin read(a2[i]);dec(a2[i]);end;

 

  filldword(f,sizeof(f)>>2,maxlongint);

  f[l1+1,l2+1]:=0;

  for i:=l1 downto 1 do

    for j:=l2 downto 1 do

      begin

        f[i,j]:=min(min(f[i+1,j],f[i,j+1]),f[i+1,j+1])+a1[i]*a2[j];

      end;

 

  writeln(f[1,1]);

 

  close(input);close(output);

end.

 

 

原文地址:https://www.cnblogs.com/SueMiller/p/2130915.html