题解 P5761 [NOI1997] 最佳游览
时间限制 内存限制
1.00s 128.00MB
题目描述
有一座旅游城,它的街道成网格状(如图).其中东西向的街道是“风景线"、两旁分布着许多景观:南北向的街道都是"林萌道",两旁没有任何建筑物。由于游客众多," 风景线”被规定为单行道,游客在风景线上只能从西走到东,林荫道上则可以任意行走。
一名游客将到这座旅游城旅游。他根据自己对景观的喜好给所有的风景线打了分,分值是从 −100 到 +100 的整数,分值越大表示我们的旅游者越喜欢这条风最线上的景致。显然这位游客不可能给这座旅游城的所有风景线都打负分。
游客可以从旅游城的任一个十字路口开始游览,在任一个十字路口结束游览。我们的旅游者希望一路上游览的所有风最线的分值之和能够尽可能地大。请你写一个程序,帮助这位游客寻找一条最佳的游览路线。
输入格式
第一行是两个整数M和N ,之间用一个空格符隔开, M 表示旅游城南北向林萌道的段数, N 表示东西向风景线的段数,(\(1 \le M \le 100\),\(1 \le N \le 20000\))。
接下来的 MM 行依次给出了由北向南各条风景线的分值信息。每行有 N−1 个整数,依次表示了自西向东每段风景线的分值。同一行相邻两个数之间用一个空格隔开。
输出格式
只有一行,含一个整数,表示你的程序所找到的最佳游览路线的总分值。
输入输出样例
输入
3 6
50 47 -36 -30 23
17 19 34 -13 -8
-42 -3 43 34 45
输出
84
看到题解里很多说最大子段和的,此题的确用到了最大子段和。但我想带大家一起思考一下为什么会想到最大子段和,然后再来做题。
首先,题目中说:
游客在东西向上只能从西走到东,南北向上则可以任意行走。
也就是说,在左右方向上我们只能向前走不回头,而上下方向上可以任意的选择,选择一条从左到右走的路线,使得这条路线上的和最大。
1.由于必须从左向右走,所以我们所走的路程必须是连续的。
2.由于是选择一条风景线,所以每个上下方向上选择一个风景。这样,每一列就化为一个风景,由此转换成一段路线。在这段路线中找最短的路线。
综合两点,在一条线段中找一段连续的和最大的子段,不就是找最大子段和嘛。
知道了要找最大子段和,事情就好办多了。但是,我们怎么来寻找一条能让我们求最大子段和的线段呢?
换句话说,在上下方向中,我们怎么选择我们要观看的那个风景点呢?
这里,我们用到贪心的思路,由于我们要求最大和,上下方向的行走是任意的,也就是我们挑选哪个都可以,我们为什么不比较一下求一个 \(max\) 呢?
for(int i=1;i<=m-1;i++)
for(int j=1;j<=n;j++)
f[i]=max(f[i],sz[j][i]);
然后就回到求最大子段和的问题了。我们一般采用dp的解决方法,设f[i]表示以第i个位置的数结尾的最大和,转移的话
看代码:
for(int i=1;i<=m-1;i++)
if(f[i-1]>0)
f[i]+=f[i-1];
如果上一个位置的值大于零的话,我们就加上,若小于零,加上只会让我们的答案变得更劣,还不如就只有我们自个儿呢。
结合图片理解一下:
由此,就可以解决掉这个题了。
偷偷说句:这个题真的有毒,题目中的例子和样例给的都不对!!!
PS:好像有人说卡快读,但我写了快读好像没被卡。
附完整代码:
#include<cstdio>
#include<cstring>
#include<iomanip>
#define INF 0x3f3f3f3f
using namespace std;
int n,m;
int sz[105][20005],f[20005],maxx=-INF;
int read()
{
int f=0,s=0;
char c=getchar();
while(!isdigit(c)) f|=(c=='-'),c=getchar();
while(isdigit(c)) s=(s<<1)+(s<<3)+c-'0',c=getchar();
return f?-s:s;
}
int main()
{
memset(f,-INF,sizeof(f));
n=read(),m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m-1;j++)
{
sz[i][j]=read();
}
for(int i=1;i<=m-1;i++)
for(int j=1;j<=n;j++)
{
f[i]=max(f[i],sz[j][i]);//化成一条路线
}
for(int i=1;i<=m-1;i++)
{
if(f[i-1]>0)
{
f[i]+=f[i-1]; //最大字段和
}
maxx=max(maxx,f[i]);
}
printf("%d",maxx);
return 0;
}