洛谷 P1434 [SHOI2002] 滑雪(优先队列+dp / 记忆化搜索)

P1434 [SHOI2002] 滑雪icon-default.png?t=M4ADhttps://www.luogu.com.cn/problem/solution/P1434

优先队列+dp

本题状态转移方程一目了然

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

为了保证状态间无后效性,即当前dp[i][j]保存的就是这个i,j位置作为终点最长的路径长度。为了保证这一点我们从最大的数开始dp,并从大到小依次进行。我们可以用优先队列来实现这个操作。

// #pragma GCC optimize (2)
// #pragma G++ optimize (2)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#define endl '\n'
#define int long long
#define lowbit(x) x &(-x)
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define TLE(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);}
using namespace std;
typedef long long ll;
const int N = 1e2+10;
struct node
{
    int a,b,c;
    bool operator < (const node &w)const{
        return c<w.c;
    }
};
priority_queue<node,vector<node>,less<node>>q;
int a[N][N],f[N][N];
int dx[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
void solve()
{
    int n,m;
    cin>>n>>m;
    rep(i,1,n)
    {
        rep(j,1,m)
        {
        cin>>a[i][j];
        f[i][j]=1;
        q.push({i,j,a[i][j]});
        }
    }
    int maxx=-2e9;
    while(q.size())
    {
        auto t=q.top();q.pop();
        int i=t.a,j=t.b;
        for(int p=0;p<4;p++)
        {
            int x=i+dx[p][0],y=j+dx[p][1];
            if(x<=0||y<=0||x>n||y>m)continue;
            if(a[i][j]<a[x][y])f[i][j]=max(f[i][j],f[x][y]+1);
        }
        maxx=max(maxx,f[i][j]);
    }
    cout<<maxx<<endl;
}
signed main()
{
    TLE();
    int T;
    T=1;
    //scanf("%lld", &T);
    while (T--)solve();
    return 0;
}

记忆化DFS

暴力搜索一定会超时,记忆化搜索可以搜得从i,j位置出发的所能到达的最远的距离,也就是会找到一个从当前位置出发能找到最大的数,且这个最大的数与可能存在的比他大的数并不相连,然后递归回溯过程中就会更新路径上每个f[i][j]值,这就保证了每个f[i][j]在被更新过一次后,其值即为 i,j位置作为终点最长的路径长度。

// #pragma GCC optimize (2)
// #pragma G++ optimize (2)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#define endl '\n'
#define int long long
#define lowbit(x) x &(-x)
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define TLE()                        \
    {                                \
        ios::sync_with_stdio(false); \
        cin.tie(0);                  \
        cout.tie(0);                 \
    }
using namespace std;
typedef long long ll;
const int N = 1e2 + 10;
int n, m;
int a[N][N], f[N][N] = {0};
int dx[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int dfs(int i, int j)
{
    if (f[i][j])
        return f[i][j];
    f[i][j] = 1;
    for (int p = 0; p < 4; p++)
    {
        int x = i + dx[p][0], y = j + dx[p][1];
        if(x<=0||y<=0||x>n||y>m)continue;//判断边界
        if (a[x][y] > a[i][j])
            f[i][j] = max(f[i][j], dfs(x, y) + 1);
    }
    return f[i][j];
}
void solve()
{
    cin >> n >> m;
    rep(i, 1, n)
            rep(j, 1, m)
                cin >>a[i][j];
    int maxx = -2e9;
    rep(i, 1, n)
        rep(j, 1, m) maxx = max(maxx, dfs(i, j));
    cout << maxx << endl;
}
signed main()
{
    TLE();
    int T;
    T = 1;
    // scanf("%lld", &T);
    while (T--)
        solve();
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值