BACKGROUND1:我是个DP沙茶......
BACKGROUND2:我今天被放假了= =因为睡的觉太少了...所以就在家写各种OI日志了=- =
这回我们来讨论一下最大子矩阵的问题,也就是POJ1050,自己去看吧= =是鸟文的
翻译过来就是给你一个数字矩阵,让你求子矩阵的最大和......
这个题就是我也能轻松地想出O(n^4)的算法,枚举矩形对角线坐标,算算算..........
这题数据规模是100,100^4=100,000,000 TLE....
所以咱得想办法去处理这个蛋疼的问题...... 这里就引入了DP算法
咱先抛开最大子矩阵不谈,谈一下最大连续子序列和,这货是一维的,可以轻松的想出O(N^2)算法,= =
枚举........给个100000你就HLL的阵亡了....
设数组OPT[I],表示连续序列以I结尾的最大和,那么如果OPT[I-1]大于第I个数,那么OPT[I-1]+NUM[I]就是
以I结尾的最大和,否则I自己组成一个序列,OPT[I]=NUM[I]....
这样DP的框架就出来了,以取到第I个数为第I个阶段,以OPT[X]为状态,以I是加入结尾为I-1的连续序列还是
单独成一个序列为决策,这样不难得出状态转移方程 OPT[I]=MAX{NUM[I],OPT[I-1]+NUM[I]}(0<I<=N)
我们求的最大值ANS就是MAX{OPT[J]}
至此,最大连续子序列问题得到解决 时间复杂度为O(N) 即使N到100W我们也不用怕了= =
把它推广到二维,也就是咱们的题,我们注意到M*N的子矩阵和为M个长度为N的连续序列和
OH!这样我们就可以把纵列求和,然后就压缩成了一维的最大连续子序列的问题,四维降到三维
时间复杂度变成了O(N^3),这样这道题就可以轻松A掉啦!
我附上我的沙茶代码,莫拍砖:
program poj1050; var n,ans:longint; num,sum:array[0..100,0..100] of longint; opt:array[0..100] of longint; procedure pre; var i,j:longint; begin fillchar(sum,sizeof(sum),0); ans:=0; readln(n); for i:=1 to n do for j:=1 to n do begin read(num[i,j]); sum[i,j]:=sum[i-1,j]+num[i,j];//求纵列和 end; end; function max(a,b:longint):longint; begin if a > b then exit(a) else exit(b); end; procedure sol; var i,j,k:longint; begin for i:=1 to n do for j:=i to n do begin fillchar(opt,sizeof(opt),0); for k:=1 to n do begin opt[k]:=max(sum[j,k]-sum[i-1,k],opt[k-1]+sum[j,k]-sum[i-1,k]); ans:=max(ans,opt[k]); end; end; end; procedure outprint; //编程习惯.... begin writeln(ans); end; procedure main; //编程习惯.... begin pre; sol; outprint; end; begin main; end.
下面还有一道类似的题,AHOI2007宝库通道
题目概述:宝库通道(rock)
探宝的旅程仍然继续中,由于你的帮助,小可可成功点燃了灯阵,避过了许多致命的陷阱,终于来到了宫殿的正厅
中。大厅的地面是由一块块大小一致的正方形石块 组成的,这些石块分为黑、白两色,组成了一个m*n的矩形,
在其中一个石块的下面就是通往藏宝库的通道。小可可不可能一个一个石块的尝试,因为有些石块安 装了机关,
一碰就会触发,整个宫殿也随之倒塌。根据藏宝图记载,通道在某一特定的区域中,这个区域是一个由数个石块
组成的面积不为0的小矩形,它的四条边 与大厅地面的边平行。如果对整个大厅地面任意划分矩形,那么在
所有矩形中,这个区域的黑色石块数目减去白色石块数目所得的差是最大的。
小可可希望和你分工,由他来选择区域,你来计算黑、白两色石块的数目差S。这样就能快速而准确的确认通道
所在的区域。藏宝图上说这个区域中的石块都没有安 装机关,只要确定了区域,就一定能找到通道。宝藏就
在眼前了,加油吧!
(用1表示黑色石块,用0表示白色石块)
输入(rock.in):输入文件的第一行为两个整数m,n (1<=m,n<=400).
以下m行,每行n个字符,每个字符都是0或1。
输出(rock.out):输出文件仅一个数,表示所有可能的区域中S值(见前文描述)最大的一个,输出这个值即可。
由于白色石块是0,他让求数目差,所以我们可以把所有的0都赋成-1,这样就跟上面的题一样啦!运用刚才的
思想,这道题是不是易如反掌?
附上本沙茶的小破程序:
program rock; var m,n,ans:longint; sum:array[0..400,0..400] of longint; map:array[0..400,0..400] of integer; f:array[0..400] of longint; procedure pre; var s:char; i,j:longint; begin readln(m,n); ans:=0; fillchar(sum,sizeof(sum),0);//纵列和 for i:=1 to m do begin for j:=1 to n do begin read(s); if s = '1' then map[i,j]:=1 else map[i,j]:=-1; sum[i,j]:=sum[i-1,j]+map[i,j]; end; readln; end; end; function max(a,b:longint):longint; begin if a > b then exit(a) else exit(b); end; procedure sol; var i,j,k:longint; begin for i:=1 to m do for j:=i to m do begin fillchar(f,sizeof(f),0); for k:=1 to n do begin f[k]:=max(f[k-1]+sum[j,k]-sum[i-1,k],sum[j,k]-sum[i-1,k]); ans:=max(ans,f[k]); end; end; end; procedure outprint; begin writeln(ans); end; procedure main; begin pre; sol; outprint; end; begin main; end.