题意简述
首先这是个东方题。。。
灵梦在一个矩阵上,每次珂以往下走一格(竖向),并且珂以横向瞬移(不需要时间,只能一次,也不会途径任何点,直接到终点)<=T步,求走到最后一行路径上的点权值和最大。
(给定矩阵的方式类似稀疏矩阵,k行,每行3个数x,y,z表示在点(x,y)上权值为z。其余权值为0.矩阵大小:两条边长均<=4000)
数据
输入
3 3 4 1
1 1 3
1 2 1
2 2 3
3 3 3
输出
9
⑨//这个是我为了娱乐而写的,不是实际输出
解释
(我自己画的,好心吧。。。)
思路
一个暴力的思路:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示走到
i
,
j
i,j
i,j的最大权值和,则
d
p
[
i
]
[
j
]
=
m
a
x
dp[i][j]=max
dp[i][j]=max{
d
p
[
i
−
1
]
[
k
]
(
−
t
<
=
k
<
=
t
)
dp[i-1][k](-t<=k<=t)
dp[i−1][k](−t<=k<=t)}
+
m
p
[
i
]
[
j
]
+mp[i][j]
+mp[i][j],其中
m
p
[
i
]
[
j
]
mp[i][j]
mp[i][j]是记录矩阵的(Q:为什么不用
m
a
p
map
map?A:会重名,
m
a
p
map
map是一种开挂的类型,前面见过)。
珂是这样是
O
(
n
m
t
)
O(nmt)
O(nmt)的,直接就过 TLE了好么。。。
有一个题目叫滑动窗口,就是一个定长度的窗口从左到右滑,每次求最大值。
仔细观察一下我们的
d
p
dp
dp数组,会发现和那个题目有点像。
不像?举个栗子:
假如每行有 10 10 10个数, t = 1 t=1 t=1,现在 D P DP DP到了第 i i i行。设 M ( l , r ) M(l,r) M(l,r)为 d p [ i − 1 ] [ l ] dp[i-1][l] dp[i−1][l]到 d p [ i − 1 ] [ r ] dp[i-1][r] dp[i−1][r]中的最大值。
那么, d p [ i ] dp[i] dp[i]应该从 1 1 1到 10 10 10依次是:
S ( 1 , 2 ) , S ( 1 , 3 ) , S ( 2 , 4 ) , S ( 3 , 5 ) , S ( 4 , 6 ) , S ( 5 , 7 ) , S ( 6 , 8 ) , S ( 7 , 9 ) , S ( 8 , 10 ) , S ( 9 , 10 ) S(1,2),S(1,3),S(2,4),S(3,5),S(4,6),S(5,7),S(6,8),S(7,9),S(8,10),S(9,10) S(1,2),S(1,3),S(2,4),S(3,5),S(4,6),S(5,7),S(6,8),S(7,9),S(8,10),S(9,10)。
诶?这个除了两边边界不太一样,别的真的都好像哦!
更准确的说,这应该跟接近于洛谷P1440,与滑动窗口不同的是,这个题目如果长度不足就全部考虑,就和我们的转移方程是一样的了。
那么现在我们就珂以维护一个优先队列, O ( m ) O(m) O(m)转移出第 i i i行所有的 d p dp dp值。那这样我们的总时间复杂度就是 O ( n m ) O(nm) O(nm)的了,把 t t t整个去掉了,准确来讲是优化掉了。
代码:
#include<bits/stdc++.h>
#define N 4010
using namespace std;
int mp[N][N];
int n,m,k,t;
void Input()
{
scanf("%d%d%d%d",&n,&m,&k,&t);
for(int i=1;i<=k;i++)
{
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
mp[x][y]=w;//暴力存
}
}
int dp[N][N];
int Q[N][2];//单调队列
void Solve()
{
for(int i=1;i<=n;i++)
{
int head=1,tail=0;
//这里如果习惯不一样,千万不要乱抄。如果要抄,那么一定要注意head和tail的边界问题!!!
//!!!
//!!!
for(int j=1;j<=min(m,t);j++)//把长度为t的先弄好(min(m,t)是防爆用的)
{
while(Q[tail][0]<=dp[i-1][j] and head<=tail)//不断去掉队尾小的
{
tail--;
}
++tail;
Q[tail][0]=dp[i-1][j];
Q[tail][1]=j;//然后加入队列
}
for(int j=1;j<=m;j++)
{
if (j+t<=m)//开始滑动!
{
while(Q[tail][0]<=dp[i-1][j+t] and head<=tail)
{
tail--;
}
++tail;
Q[tail][0]=dp[i-1][j+t];
Q[tail][1]=j+t;
//加入队列
}
if (Q[head][1]<j-t)//及时踢出队列
{
++head;
}
dp[i][j]=Q[head][0]+mp[i][j];//设置答案
}
}
int ans=-1;
for(int i=1;i<=m;i++)
{
ans=max(ans,dp[n][i]);
}
printf("%d\n",ans);
//然后这样就是O(nm)了。。。
}
main()
{
Input();
Solve();
return 0;
}