树塔狂想曲

树塔狂想曲

题目

相信大家都在长训班学过树塔问题,题目很简单求最大化一个三角形数塔从上往下走的路径和。走的规则是:(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。

输入样例

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

输出样例

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!

数据范围

在这里插入图片描述

思路

这道题用dp来做。
我们先预处理出前缀和,以求出不ban任何点的最大路径和。
求出之后,就可以通过这个求出进过点i,j的最大值,从而求出每一列的最大值和次大值。
那么我们读入每一个被ban掉的点的时候,就可以看是不是最大值。不是就输出最大值,否则输出次大值

代码

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,m,a[1001][1001],f[1001][1001],f1[1001][1001],f2[1001][1001],an[1001],an1[1001],x,y,h;
int read()//快读
{
	int ans=0;char c=getchar();
	while (c>='0'&&c<='9')
	{
		ans=ans*10+c-48;
		c=getchar();
	}
	return ans;
}
void write(int hh)//快输
{
	if (hh<0) putchar('-'),hh=-hh;
	if (hh>9) write(hh/10);
	putchar(hh%10+48);
}
int main()
{
	n=read();m=read();//读入
	for (int i=1;i<=n;i++)
	for (int j=1;j<=i;j++)
	{
		a[i][j]=read();//读入
		f[i][j]=max(f[i-1][j],f[i-1][j-1])+a[i][j];//求前缀和
	}
	for (int i=n;i>=1;i--)
	for (int j=1;j<=i;j++)
	f1[i][j]=max(f1[i+1][j],f1[i+1][j+1])+a[i][j];//先不ban点求做大路径和
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=i;j++)
		{
			f2[i][j]=max(f[i-1][j],f[i-1][j-1])+max(f1[i+1][j],f1[i+1][j+1])+a[i][j];//求出经过点i,j的最大值
			if (f2[i][j]>an[i])
			{
				an[i]=f2[i][j];//求出第i行的最大值
				h=j;//标记第i行最大值进过的是第i行的第几位
			}
		}
		for (int j=1;j<=i;j++)
		if (h!=j) an1[i]=max(an1[i],f2[i][j]);//求出第i行的次大值
	}
	for (int i=1;i<=m;i++)
	{
		x=read();y=read();//读入
		if (x==1&&y==1)//起始点被ban掉
		{
			putchar('-');//输出-1
			putchar('1');
		}
		else if (f2[x][y]!=an[x]) write(an[x]);//ban掉的点不是最大值,输出最大值
		else write(an1[x]);//ban掉的点事最大值,输出次大值
		putchar('\n');//输出换行
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值