五一模拟赛 JZOJ 4746 树塔狂想曲 线性DP

版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/89763485

title

【问题描述】

相信大家都写过数字三角形问题,题目很简单求最大化一个三角形数塔从上往下走的路径和。走的规则是:(i,j)号点只能走向(i+1,j)或者(i+1,j+1)。如下图是一个数塔,映射到该数塔上行走的规则为:从左上角的点开始,向下走或向右下走直到最底层结束。

       1
       3 8
       2 5 0
       1 4 3 8
       1 4 2 5 0

路径最大和是1+8+5+4+4 = 22,1+8+5+3+5 = 22或者1+8+0+8+5 = 22。
小S觉得这个问题so easy。于是他提高了点难度,他每次ban掉一个点(即规定哪个点不能经过),然后询问你不走该点的最大路径和。
当然他上一个询问被ban掉的点过一个询问会恢复(即每次他在原图的基础上ban掉一个点,而不是永久化的修改)。

【输入】

第一行包括两个正整数,N,M,分别表示数塔的高和询问次数。
以下N行,第i行包括用空格隔开的i - 1个数,描述一个高为N的数塔。
而后M行,每行包括两个数X,Y,表示第X行第Y列的数塔上的点被小S ban掉,无法通行。
(由于读入数据较大,c或c++请使用较为快速的读入方式)

【输出】

M行每行包括一个非负整数,表示在原图的基础上ban掉一个点后的最大路径和,如果被ban掉后不存在任意一条路径,则输出-1。

【输入输出样例1】

tower. in
5 3
1
3 8
2 5 0
1 4 3 8
1 4 2 5 0
2 2
5 4
1 1
tower.out
17
22
-1

【样例解释】

第一次是
1
3 X
2 5 0
1 4 3 8
1 4 2 5 0
1+3+5+4+4 = 17 或者 1+3+5+3+5=17
第二次:
1
3 8
2 5 0
1 4 3 8
1 4 2 X 0
1+8+5+4+4 = 22
第三次:你们都懂的!无法通行,-1!

扫描二维码关注公众号,回复: 6124365 查看本文章

【数据范围】

在这里插入图片描述

analysis

  • 看到数字三角形,我笑了,毕竟 D P DP 入门题目,再往下看,我的笑容逐渐凝固。。

  • 但还是套上数字三角形的板子,标记了被 b a n ban 掉的点,在线做,样例就很容易的过了,然而时间复杂度是 O ( m n 2 ) O(m*n^2) ,肯定 T L E TLE ,那就只能想其他方法了。

  • 然后我们可以发现: 我们可以 n 2 n^2 去预处理一下,
    f [ i , j ] f[i,j] 表示 [ 1 , 1 ] [1,1] 到点 [ i , j ] [i,j] 的最大路径和。
    g [ i , j ] g[i,j] 则表示 [ i , j ] [i,j] 最后一层 的最大路径和。

    然后我们这样就可以发现:
    f [ i , j ] + g [ i , j ] a [ i , j ] f[i,j]+g[i,j]-a[i,j] 就是从第 1 1 层到第 n n 层经过点 [ i , j ] [i,j] 的最大路径和!
    然后我们每一层找一个左边点 [ i , j 1 ] [i,j-1] 最大的 f [ i , j ] + g [ i , j ] a [ i , j ] f[i,j]+g[i,j]-a[i,j]
    跟一个右边点 [ i , j + 1 ] [i,j+1] 最大的。
    这样查询的时间为 O ( 1 ) O(1) ,时间复杂度就变成了 O ( n 2 + m ) O(n^2+m)

  • 看起来很简单,仔细想想后确实也不难,然而我还是想了快一个小时,还是慢慢提高吧。。

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+10;

template<typename T>inline void read(T &x)
{
	x=0;
	T f=1, ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}

int a[maxn][maxn];
int f[maxn][maxn];//f[i][j]表示从上往下走到(i,j)的最大路径和
int g[maxn][maxn];//g[i][j]表示从下往上走到(i,j)的最大路径和
int l[maxn][maxn];//(i,j)左边点的最大值
int r[maxn][maxn];//(i,j)右边点的最大值
int main()
{
	freopen("tower.in","r",stdin);
	freopen("tower.out","w",stdout);
    int n,m;
    read(n);read(m);
    for (int i=1; i<=n; ++i)
        for (int j=1; j<=i; ++j)
            read(a[i][j]),
            f[i][j]=max(f[i-1][j-1],f[i-1][j])+a[i][j];
    for (int i=n; i>=1; --i)
		for (int j=i; j>=1; --j)
    		g[i][j]=max(g[i+1][j+1],g[i+1][j])+a[i][j];
    for (int i=1; i<=n; ++i)
    {
    	for (int j=1; j<=i; ++j) l[i][j]=max(l[i][j-1],f[i][j]+g[i][j]-a[i][j]);
    	for (int j=i; j>=1; --j) r[i][j]=max(r[i][j+1],f[i][j]+g[i][j]-a[i][j]);
	}
	while (m--)
	{
		int x,y;
		read(x);read(y);
		if (x==1 && y==1) puts("-1");
		else printf("%d\n",max(l[x][y-1],r[x][y+1]));
	}
    return 0;
}

猜你喜欢

转载自blog.csdn.net/huashuimu2003/article/details/89763485
今日推荐