【NOIP2014】子矩阵

这题如果按暴力做只有一半分,最大时间复杂度为O(C(16,8)*C(16,8)); 很容易算出超时;

我们可以发现如果直接dp会很难想,但是知道选哪几行去dp就很好写状态转移方程:

     dp[i][j]=min(dp[i][j],dp[k][j-1]+a[i]+b[k][i]);

其中dp[i][j]表示 前i列里选j列的子矩阵最大分值

     a[i]表示 第i列选到的行的总差值

     b[k][i]表示选到的每一行第k列和第i列之间的差值

我们只要枚举 行 然后dp一次,取最小值即可 这样最大时间复杂度就成了O(C(8,16)*n3);

最后附上我弱弱的pascal代码: 

 1 var
 2   i,j,k,n,m,n1,m1,ans:longint;
 3   dp,a,f:array[0..16,0..16] of longint;
 4   hc:array[0..16,1..16,1..16] of longint;
 5   b,lc:array[0..17] of longint;
 6 function min(a,b:longint):longint;
 7  begin
 8   if a>b then min:=b
 9          else min:=a;
10  end;
11 procedure ddp;
12  var
13   i,j,k,max:longint;
14  begin
15   fillchar(f,sizeof(f),0);
16   fillchar(lc,sizeof(lc),0);
17   fillchar(dp,sizeof(dp),0);
18   for i:=1 to m do
19    for j:=1 to n1-1 do
20     lc[i]:=lc[i]+abs(a[b[j+1],i]-a[b[j],i]);
21   for i:=1 to m do
22    for j:=i+1 to m do
23     for k:=1 to n1 do
24      f[i,j]:=f[i,j]+hc[b[k],i,j];
25   for i:=1 to m do
26    dp[i,1]:=lc[i];
27   for i:=2 to m do
28    for j:=2 to m1 do
29     if i>=j then
30               begin
31                dp[i,j]:=maxlongint;
32                for k:=j-1 to i-1 do
33                 dp[i,j]:=min(dp[i,j],dp[k,j-1]+lc[i]+f[k,i]);
34               end;
35   for i:=m1 to m do
36    if dp[i,m1]<ans then ans:=dp[i,m1];
37  end;
38 procedure jw(ii:longint);
39  begin
40   inc(b[ii]);
41   if ii>=0 then
42    if b[ii]>(n-n1+ii) then
43                          begin
44                           jw(ii-1);
45                           b[ii]:=b[ii-1]+1;
46                          end;
47  end;
48 begin
49   read(n,m,n1,m1);
50   for i:=1 to n do
51    for j:=1 to m do
52     begin
53      read(a[i,j]);
54      for k:=1 to j-1 do
55       hc[i,k,j]:=abs(a[i,j]-a[i,k]);
56     end;
57   for i:=1 to n1 do
58    b[i]:=i;
59   ans:=100000000;
60   while b[0]=0 do
61    begin
62     ddp;
63     jw(n1);
64    end;
65   write(ans);
66 end.

 

posted @ 2016-09-13 19:11  JSC!  阅读(1483)  评论(0编辑  收藏  举报