题解 P3800 【Power收集】

· · 题解

先无良宣传一下博客 wwwwww
文章列表 - 核融合炉心 - 洛谷博客

知识点 : DP 优化 , 单调队列

上代码:

由于使用了手写数组模拟队列 ,
清空时只需重置头尾指针 , 及队首元素即可 .
常数吊打 deque

#include<cstdio>
#include<ctype.h>
#include<cstring>
#define int long long
#define max(a,b) a>b?a:b
//=====================================
const int MARX = 4e3+10;
int n,m,k,t, now,ans;
int f[MARX][MARX];
int head=1,tail=1;//手写双端队列 
int q[MARX]={9223372036854775807};//为q[0]赋一个极大值,来防止插入元素时越界 
//=====================================
inline int read()
{
    int fl=1,w=0;char ch=getchar();
    while(!isdigit(ch) && ch!='-') ch=getchar();
    if(ch=='-') fl=-1;
    while(isdigit(ch)){w=w*10+ch-'0',ch=getchar();}
    return fl*w;
}
void in(int x)//向单调队列中插入元素 
{
    while(f[now-1][x]>f[now-1][q[tail]] && tail>=head) 
      tail--;//取出队尾小于插入元素的数 , 以保证单调性 
    q[++tail]=x;//插入队尾 
}
int find(int x)//查询元素 
{
    if(x+t<=m)in(x+t);//将能转移到x点 的 最后一个元素 x+t 插入队列 
    while(q[head]+t<x) head++;//找到队首第一个能够转移到x的点 
    return q[head]; 
}
//=====================================
signed main()
{
    n=read(),m=read(),k=read(),t=read();
    while(k--)
    {
      int x=read(),y=read(),w=read();
      f[x][y]=w;
    }

    for(now=2;now<=n;now++)//从第二行,开始转移 
    {
      for(int i=1;i<=t;i++) in(i);//初始化单调队列 , 满足能够 转移j=1的点 
      for(int j=1;j<=m;j++) f[now][j]+=f[now-1][find(j)];// 进行转移 
      head=tail=1 , q[1]=0;//清空队列 
    }

    for(int i=1;i<=m;i++)//取得最大值 
      ans=max(ans,f[n][i]);
    printf("%lld",ans);
}

完成了这篇题解 , 东方众信仰 ++