Luogu P1434 滑雪 题解

P1434 滑雪 题解

感觉这题好水 我是蒟蒻

题目传送门:P1434

这道题就是妥妥的动规

图论背模板,数论背公式,动规背方程,高精背代码

一、定义状态

dp[i][j]走到点(i,j)的长距离(包括点(i,j)

二、分析问题

对于一个点(i,j),显然有以下四种可能的走法:

向上 向下 向左 向右

那么只要目标点可走,就可以计算目标点的dp

三、转移方程

dp[i][j](i,j)dp,a[i][j](i,j)

dp[i+1][j]=max(dp[i][j]+1,dp[i+1][j])(a[i][j]>a[i+1][j])

dp[i-1][j]=max(dp[i][j]+1,dp[i-1][j])(a[i][j]>a[i-1][j])

dp[i][j+1]=max(dp[i][j]+1,dp[i][j+1])(a[i][j]>a[i][j+1])

dp[i][j-1]=max(dp[i][j]+1,dp[i][j-1])(a[i][j]>a[i][j-1])

为什么有四条

因为这是在一张图中找最长路,所以应该用DFS(Depth First Search)扫描整张图

同时由于题目没有给出起点,所以要枚举每一个点作为起点

所以只需要设置起点的dp为1,然后DFS就好了

四、优化

我们发现,如果枚举每一个点作为起点,每次都搜索整张图的话肯定TLE掉。

所以要考虑一下如何优化。

我们发现,好像从最高的地方开始下滑得到的长度最大?

试一下 试试就逝世

#include<bits/stdc++.h>
#define GO(from,to,var) for(int var=from;var<=to;var++)
#define GON(from,to,var) for(int var=from;var<to;var++)
#define GOGRA(EDGE,HEAD,var,u) for(int var = HEAD[u];var;var=EDGE[var].next)
#define COMP(__type,__member,__comp) [=](__type a1,__type a2)->bool{return a1.__member __comp a2.__member;}
using namespace std;
typedef int NUM;
const NUM maxn = 1e7;const NUM maxm = 1e7;
void Read();void Solve();
inline NUM fr(){
    NUM x = 0, dis = 1;
    char ch = getchar();
    while(ch<'0'||ch>'9'){
        if(ch == '-')dis = -1;
        ch = getchar();}
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch = getchar();}
    return x*dis;
}
int a[3000][3000];
int dp[3000][3000];
int stn,stm,n,m,maxx;

inline void dfs(int nn,int mm){
    if(nn>0&&mm>0&&nn<=n&&mm<=m){//合法的点
        maxx=max(maxx,dp[nn][mm]);
        if(a[nn+1][mm]<a[nn][mm]&&dp[nn+1][mm]<dp[nn][mm]+1){
            dp[nn+1][mm]=dp[nn][mm]+1;
            dfs(nn+1,mm);
        }
        if(a[nn-1][mm]<a[nn][mm]&&dp[nn-1][mm]<dp[nn][mm]+1){
            dp[nn-1][mm]=dp[nn][mm]+1;
            dfs(nn-1,mm);
        }
        if(a[nn][mm+1]<a[nn][mm]&&dp[nn][mm+1]<dp[nn][mm]+1){
            dp[nn][mm+1]=dp[nn][mm]+1;
            dfs(nn,mm+1);
        }
        if(a[nn][mm-1]<a[nn][mm]&&dp[nn][mm-1]<dp[nn][mm]+1){
            dp[nn][mm-1]=dp[nn][mm]+1;
            dfs(nn,mm-1);
        }
    }
}



void Read(){
    memset(a,0x3f,sizeof(a));
    n=fr(),m=fr();int maxxx=0;
    GO(1,n,i)GO(1,m,j){
        a[i][j]=fr();
        if(maxxx<a[i][j]){
            maxxx=a[i][j];
            stn=i;stm=j;
        }
    }
}
void Solve(){
    dp[stn][stm]=1;
    dfs(stn,stm);
    printf("%d",maxx);
}
int main(){
    Read();
    Solve();
    return 0;
}

50pts

这是为什么呢?

img

不要急,我们先来构造一组数据:

5 5
1 2 3 4 5
1 1 1 1 1
1 1 1 9 1
1 1 1 1 1
1 1 1 1 1

我们发现,虽然(3,4)是最高的点,但是最长路径的起点显然应该是(1,5)

难道我们DFS真的只能眼睁睁的看着它TLE了吗?

不要慌,我们仔细思考一下:

在跑第一遍DFS的时候,我们就已经处理了一部分点的最长长度。换言之,一部分点的dp已经求出来了(未必正确)。

所以,我们只需要把未处理过的点当成起点跑DFS就可以了。

注意起点的dp=1哟。

五、AC代码

我知道你们就想要这个

#include<bits/stdc++.h>
#define GO(from,to,var) for(int var=from;var<=to;var++)
#define GON(from,to,var) for(int var=from;var<to;var++)
#define GOGRA(EDGE,HEAD,var,u) for(int var = HEAD[u];var;var=EDGE[var].next)
#define COMP(__type,__member,__comp) [=](__type a1,__type a2)->bool{return a1.__member __comp a2.__member;}
using namespace std;
typedef int NUM;
const NUM maxn = 1e7;const NUM maxm = 1e7;
void Read();void Solve();
inline NUM fr(){
    NUM x = 0, dis = 1;
    char ch = getchar();
    while(ch<'0'||ch>'9'){
        if(ch == '-')dis = -1;
        ch = getchar();}
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch = getchar();}
    return x*dis;
}
//个人模板

int a[3000][3000];//原图
int dp[3000][3000];//dp[i][j]-> 走到(i,j)的最长路
int n,m,maxx;//maxx记录答案

inline void dfs(int nn,int mm){
    if(nn>0&&mm>0&&nn<=n&&mm<=m){//合法的点
        maxx=max(maxx,dp[nn][mm]);//记录
        if(a[nn+1][mm]<a[nn][mm]&&dp[nn+1][mm]<dp[nn][mm]+1){//可以向下
            dp[nn+1][mm]=dp[nn][mm]+1;
            dfs(nn+1,mm);
        }
        if(a[nn-1][mm]<a[nn][mm]&&dp[nn-1][mm]<dp[nn][mm]+1){//可以向上
            dp[nn-1][mm]=dp[nn][mm]+1;
            dfs(nn-1,mm);
        }
        if(a[nn][mm+1]<a[nn][mm]&&dp[nn][mm+1]<dp[nn][mm]+1){//可以向右
            dp[nn][mm+1]=dp[nn][mm]+1;
            dfs(nn,mm+1);
        }
        if(a[nn][mm-1]<a[nn][mm]&&dp[nn][mm-1]<dp[nn][mm]+1){//可以向左
            dp[nn][mm-1]=dp[nn][mm]+1;
            dfs(nn,mm-1);
        }
    }
}

void Read(){
    memset(a,0x3f,sizeof(a));//初始化
    n=fr(),m=fr();
    GO(1,n,i)GO(1,m,j){//读入数据
        a[i][j]=fr();
    }
}
void Solve(){
    GO(1,n,i)GO(1,m,j){
        if(dp[i][j]==0){
            dp[i][j]=1;//起点的dp至少为1
            dfs(i,j);
        }
    }
    printf("%d",maxx);//输出
}
int main(){
    Read();
    Solve();
    return 0;
}
posted @   Locked_Fog  阅读(44)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
主题色彩