Submatrix.
对于这道题,我讲过之后很多同学还是一脸懵逼。而我感觉我对这道题的理解也不是非常的透彻以至于我也没有帮助其他同学A掉这道题。
这次我要重新在这里系统的分析一下这道题,也算是给自己再讲了一遍了。
“对于多维问题,常见的思路是降维”——RujiaLiu。
虽然本题只有二维,但我们也可以把它降到一维试一试。此时问题变成了:对于一个有n个元素的一维序列,求一个长度为m(1≤m≤n)的子序列,使得其这个子序列的分值最小。我们如何用更低代价的算法解决问题呢?可以尝试DP。
事实上,这个问题具有最有子结构和无后效性。我们可以把状态定义为“以原序列的第j个元素结尾的长度为i的子序列的分值”。这样,不难列出状态转移方程: f[i,j]:=min{f[i-1,k]+|a[j]-a[k]|}(i≤k
Program submatrix;
constoo=2000000000;//初始化“无限大”的值,这里没有用maxlongint,避免DP时溢出
type data=array[0..20] of longint;
var
i,j,ans,n,m,r,c:longint;
a:array[0..20,0..20] of longint;
rb:data;
function min(x,y:longint):longint;
begin
if x>=y then exit(y) else exit(x)
end;
procedure dp;//动规列
var
t,i,k:longint;
f,hc:array[0..20,0..20] of longint;
zc:array[0..20] of longint;
begin
//为了后面将整个列放在一起操作,初始化
fillchar(hc,sizeof(hc),0);//横差
fillchar(zc,sizeof(zc),0);//纵差
rb[r+1]:=rb[r];
for i:=1 to m do
for j:=1 to r do
zc[i]:=zc[i]+abs(a[rb[j],i]-a[rb[j+1],i]);//每一列中的纵差
for i:=1 to m-1 do
for j:=i+1 to m do
for k:=1 to r do
hc[i,j]:=hc[i,j]+abs(a[rb[k],i]-a[rb[k],j]);//每两列之间的横差fillchar(f,sizeof(f),0);
//------DP主体---------
for i:=1 to m do f[1,i]:=zc[i];
for i:= 2 to c do//枚举阶段
for j:=i to m do begin//枚举状态
t:=oo; //t用来临时存储f[i,j]
for k:=i-1 to j-1 do//计算状态(以一个O(m)循环为代价
t:=min(t,f[i-1,k]+hc[k,j]);
f[i,j]:=t+zc[j]
end;
for i:=c to m do ans:=min(ans,f[c,i]);
end;
proceduredfs(k,p:longint);//穷搜行
var i:longint;
begin
if k=rthendp;
for i:=p+1 to n do begin
rb[k+1]:=i;dfs(k+1,i)
end
end;
Begin
assign(input,'submatrix.in'); reset(input);
assign(output,'submatrix.out');rewrite(output);readln(n,m,r,c);
for i:=1 to n do begin
for j:=1 to m do
read(a[i,j]);
readln
end;
ans:=oo;
dfs(0,0);
writeln(ans);
close(input);
close(output)
End.