旅行
题目
ACM队员们到Z镇游玩,Z镇是一个很特别的城镇,它有m+1条东西方向和n+1条南北方向的道路,划分成MN个区域。Z镇的名胜位于这些区域内,从上往下第i行,从左往右数第j列的区域记为D(i,j)。ACM队员们预先对这MN个区域打分V(i,j)(分数可正可负)。分数越高表示他们越想到那个地方,越低表示他们越不想去。为了方便集合,队员们只能在某一个范围内活动。我们可以用(m1,n1)与(m2,n2)(m1<=m2,n1<=n2)表示这样一个范围:它是这些区域的集合:ACM队员希望他们活动区域的分值总和最大。
当然,有的队员可能一个也不去(例如,所有区域的分值都是负数。当然,如果某范围内的分值和为0的话,他们也不会去玩)。也有可能他们游览整个Z镇。你的任务是编写一个程序,求出他们的活动范围(m1,n1),(m2,n2)。
输入
输入有m+1行,第一行有两个整数m,n(m,n定义如上)。其中( ),接下来的m行,每行n个整数,第i行第j个数表示分数V(i,j)。(-128<=v(i,j)<=127)每两个整数之间有一个空格。
输出
输入只一行,分两种情况:
1. 队员在范围内(m1,n2)(m2,n2)内活动,输出该范围内的分值。
2. 队员们任何地方都不去,只需输出NO。
注意:不要输出多余的空行,行首行尾不要有多余的空格。
Sample Input
样例1
4 5
1 -2 3 -4 5
6 7 8 9 10
-11 12 13 14 -15
16 17 18 19 20
样例2
2 3
-1 -2 -1
-4 -3 -6
1
2
Sample Output
样例1
146
样例2
NO
思路
这题求的是最大前缀和矩阵。
输入后将第一行和第一列预处理一下,及求前缀和。
预处理前:
1 2 3 1
1 1 1 2
2 1 1 1
预处理后:
1 3 6 7
2 1 1 2
4 1 1 1
f
[
i
]
[
j
]
表
示
从
f
[
1
]
[
1
]
到
f
[
i
]
[
j
]
的
和
f[i][j]表示从f[1][1]到f[i][j]的和
f[i][j]表示从f[1][1]到f[i][j]的和
即
矩
阵
(
1
,
1
)
,
(
i
,
j
)
的
总
和
即矩阵(1,1),(i,j)的总和
即矩阵(1,1),(i,j)的总和
状态转移方程:
f
[
i
]
[
j
]
+
=
f
[
i
−
1
]
[
j
]
+
f
[
i
]
[
j
−
1
]
−
f
[
i
−
1
]
[
j
−
1
]
f[i][j]+=f[i-1][j]+f[i][j-1]-f[i-1][j-1]
f[i][j]+=f[i−1][j]+f[i][j−1]−f[i−1][j−1]
1
<
=
i
<
=
n
,
1
<
=
j
<
=
m
1<=i<=n,1<=j<=m
1<=i<=n,1<=j<=m
意思是矩阵(1,1)到(i-1,j)加矩阵(1,1)到(i,j-1),
再减去它们重叠的部分即矩阵(1,1)到(i-1,j-1),
再加上它本身的权值就为矩阵(1,1)到(i,j)的总值了。
全部处理后上图矩阵变为:
1 3 6 7
2 5 9 12
4 8 13 17
程序
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
using namespace std;
int n,m,f[10001][10001],ans,d;
void in(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&f[i][j]);
if(f[i][j]>0){
d=1;
}
}
}if(d==0){//若无正数直接say no!
printf("NO");
exit(0);
}//下面是预处理第一行与第一列
for(int i=2;i<=n;i++){
f[i][1]+=f[i-1][1];
}for(int j=2;j<=m;j++){
f[1][j]+=f[1][j-1];
}
}void DP(){
for(int i=2;i<=n;i++){
for(int j=2;j<=m;j++){
f[i][j]+=f[i][j-1]+f[i-1][j]-f[i-1][j-1];//求前缀和矩阵
ans=max(f[i][j],ans);//记录最佳答案
}
}
}
int main(){
in();
DP();
if(ans==0){
printf("NO");
}else
printf("%d",ans);
}