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] 代表消去序列A1的i->l1,序列A2的j->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 为一个元素的和。所以每次都在 A1,A2 中选择某一序列的一个元素跟另外一个序列的一段(或一个)消去。
那么状态转移方程就变成了:
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+1,y]+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序列的i与A2序列从j开始的一段一起消去
f[i,j+1]+A1[i]*A2[j] //A1序列从i开始的一段与A2序列的j一起消去
f[i+1,j+1]+A1[i]*A2[j] } //A1序列的i与A2序列的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.