最大子矩阵 POJ1050&Ahoi2007 rock

  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.

转载于:https://www.cnblogs.com/ACoder/archive/2011/04/06/2006575.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值