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