【前言】
°朱老湿上次提了一句这个,其实挺水的,但还算挺经典的吧。简略说说接着刷题去…
【最大连续字段和】
°Problem:对{an}数列,求最大的一段子序列的和。
°Solution:F[i]←max{F[i-1]+a[i],a[i]}(1<=i<=n)
°Answer:Max{F[i]}(1<=i<=n)
°Code:
1: Program Max_1(input,output);
2: var n,i,ans:Longint;
3: F,a:array[0..100]of longint;
4: function max(a,b:Longint):Longint;
5: begin
6: if a>b then exit(a) else exit(b);
7: end;
8: begin
9: readln(n);
10: for i:=1 to n do read(a[i]);ans:=-19940805;
11: for i:=1 to n do
12: begin
13: F[i]:=max(F[i-1]+a[i],a[i]);
14: if ans<F[i] then ans:=F[i];
15: end;
16: writeln(ans);
17: end.
°Extended:在一个长度为n的数列{An}中,求m个连续子序列,使得这m个连续子序列的和最大,且m个子序列无公共元素。
°Solution:解决类似的问题,我们可以利用“加一维”的思想利用动态规划来解决。
F[i,j]表示数列前j个元素中,i个无公共元素的子序列的最大和,且必须包含第j个元素。
F[i,j]=max{F[i,j-1]+a[j](i<=j<=n),F[i-1,k]+a[j](i-1<=k<=j-1)}
时间复杂度为O(n^3)
°Code:
1: Program Max_2(input,output);
2: var n,m,ans,i,j,k:longint;
3: a:array[1..100]of longint;
4: F:array[0..100,0..100]of longint;
5: Function max(a,b:longint):longint;
6: begin
7: if a>b then exit(a) else exit(b);
8: end;
9: begin
10: readln(n,m);
11: for i:=1 to n do read(a[i]);
12: for i:=1 to m do
13: for j:=i to n do
14: begin
15: F[i,j]:=F[i,j-1]+a[j];
16: for k:=i-1 to j-1 do
17: F[i,j]:=max(F[i-1,k]+a[j],F[i,j]);
18: end;
19: ans:=-19940805;
20: for i:=1 to n do
21: if F[m,i]>ans then ans:=F[m,i];
22: writeln(ans);
23: end.
【最优子矩阵】
°Problem:对n×m的矩阵,求最大的一个子矩阵的和。
°Solution:枚举边界,将矩阵压缩成线性,用最大字段和出解
°Code:
1: Program Max_Matrix(input,output);
2: var a:array[1..100,1..100]of longint;
3: aa,F:array[1..100]of longint;
4: i,j,n,m,up,down,ans:longint;
5: Function max(a,b:longint):Longint;
6: begin
7: if a>b then exit(a) else exit(b);
8: end;
9: Procedure Zip(up,down:longint);
10: begin
11: fillchar(aa,sizeof(aa),0);
12: for i:=1 to m do
13: for j:=up to down do
14: inc(aa[i],a[i,j]);
15: end;
16: begin
17: readln(n,m);
18: for i:=1 to n do
19: for j:=1 to m do
20: read(a[i,j]);
21: ans:=-19940805;
22: for up:=1 to n do
23: for down:=1 to n do
24: begin
25: Zip(up,down);
26: fillchar(F,sizeof(F),0);
27: for i:=1 to m do
28: begin
29: F[i]:=max(F[i-1]+aa[i],aa[i]);
30: if F[i]>ans then ans:=F[i];
31: end;
32: end;
33: writeln(ans);
34: end.
°Extended:在一个n*m二维的矩阵中,确定两个小的矩阵,使这两个小矩阵中所有元素的总和最大,且两个矩阵无公共元素。
这个不会…真心复杂…分类讨论+枚举伤不起
【例题】
°Vijos 1057 House Building(这个不是最优子矩阵,但是很有趣啊~呵呵)
描述 Description
有面积为n*m的一大块土地,要在这块土地上建造一所房子,这个房子必须是正方形的。
但是,这块土地上面有很多不平坦的地方,这些地方不能用来盖房子。
他希望找到一块最大的正方形来盖房子。
输入格式 Input Format
输入文件第一行为两个整数n,m(1<=n,m<=1000)
接下来n行,每行m个数字,用空格隔开。
0表示该块土地不平坦,1表示该块土地完好。
输出格式 Output Format
一个整数,最大正方形的边长。
样例输入 Sample Input
4 4
0 1 1 1
1 1 1 0
0 1 1 0
1 1 0 1
样例输出 Sample Output
2
°Solution:F[i,j]表示以(i,j)为右下角的最大正方形的边长。
F[i,j]←min{F[i-1,j],F[i,j-1],F[i-1,j-1]}+1
注意方程要取min:
***************
***************
***************
***************
我们设f[i-1, j]=3,首先我们可以保证红色区域为1,再设f[I,j-1]=2 那么绿色区域为1
***************
***************
***************
***************
我们设f[i-1, j-1]=3,那么蓝色区域为1.
***************
***************
***************
***************
很明显,我们应该选取最小值,才可以确定以(I,j)为右下角的最大正方形长度。
这是一个经典的由方案合理性确定opt的问题。
°Code:
Program HouseBuilding(input,output);
var F,a:array[0..1000,0..1000]of integer;
n,m,i,j,ans:integer;
Function min(a,b,c:integer):integer;
begin
min:=a;if b<min then min:=b;if c<min then min:=c;
end;
begin
readln(n,m);
for i:=1 to n do
for j:=1 to m do
read(a[i,j]);
ans:=-19940805;
for i:=1 to n do
for j:=1 to m do
if a[i,j]=1 then
begin
F[i,j]:=min(F[i-1,j],F[i,j-1],F[i-1,j-1])+1;
if F[i,j]>ans then ans:=F[i,j];
end;
writeln(ans);
end.
°Strengthed House Building(可行性最优子矩阵)
描述 Description
n*m的矩形,有的位置被禁止选取,可以选取的格子价值不同。
找出一个最大的 矩形平地,使得这个矩形合法且价值最大。
输入 Input
从文件读入数据。第一行有两个整数n,m。
第i+1行的第j个数表示a[i][j].
如果这个数为0,则表示这个位置的格子被禁止。
1≤n,m≤1000 0≤a[i][j]≤255 保证输入数据都是整数。
输出 Output
最大的利用价值。
输入样例
3 4
1 2 3 4
5 0 6 3
10 3 4 0
输出样例
17
°地精工程师(价值可变最优子矩阵)
【跋】
没啥说的,就这样吧