tyvj 1004 滑雪 dfs + dp

10 篇文章 0 订阅
2 篇文章 0 订阅

题目链接


/*

先吐槽一发,真的是很容易跑题啊我...

本来是在看压位的知识,一篇博文里面推荐了tyvj里一道题(tyvj 2157)。

于是我就跑去看,一看发现去年刚转C++时写了一道题还没过(写得还特别丑),

所以今天就又捡起来写了一发。之后就跑得更远了...结果到现在还没再看压位...。

*/


题意:

给定一个 n * m 的网格,每个格子有自己的高度,从一个格子可以向上下左右走,规定只能向高度比它低的走。问最长的路径经过多少个格子。


思路:

一开始想的是每次出发 必然最好 从当前可以走的最高的出发,于是先排序。

外层套一个循环,从最高的到最低的循环。

将当前没走过的的最高的push进队列,然后bfs一发,用 dp[ ][ ] 记录该点可达的最高点到该点的路径长度,初始点为 (x0, y0), 则 dp[x0][y0] = 1。

比如说从 (x, y) 走向 (nx, ny),可用 dp[x][y] 去更新 dp[nx][ny]。

结果第 10 个点 T 了。


认真反思一下,会发现,除去 每个最开始被 push 进队列的那些点外,其他点都有可能被走很多次。

比如说上面那个 (nx, ny) 点,如果它第一次被走到的时候 dp[nx][ny] 不是最大值,那么第二次从别的 (x', y') 点走到 (nx, ny) 点就要对其更新,将其入队,再走一遍之前走过的,这显然就是计算的浪费了。

所以,我们应当追求访问完这个点时记录下来的 dp[ ][ ] 值就为最优的,而原先的做法是办不到的,因为原先的做法是用 (x, y) 去更新 (nx, ny),而这时会对 (nx, ny) 产生影响的其他点 (x', y') 还不知道在哪呢~ 所以肯定不可能在访问完 (x, y) 后就使得 (nx, ny) 最优。

那怎么办呢?


修改 dp 的含义,使之意为 该点可达的最低点到该点的路径长度。这样,访问 (x, y) 时,便是用其周围四个点中的可行点去更新 dp[x][y], 显然,访问完 (x, y) 后,其中记录的 dp 值就保证最优了。

这样一说,就很显然是 dfs 的思路了。还有记忆化。


还是当年搜索学得不扎实啊(叹


Code:

#include <cstdio>
#include <cstring>
#define maxn 110
int a[maxn][maxn], dp[maxn][maxn], r, c;
inline int max(int a, int b) { return a > b ? a : b; }
const int d[4][2] = {{-1, 0}, {1, 0}, {0, 1}, {0, -1}};
bool can(int x, int y) { return x >= 0 && x < r && y >= 0 && y < c; }
void dfs(int x, int y) {
    if (dp[x][y]) return;
    dp[x][y] = 1;
    for (int i = 0; i < 4; ++i) {
        int xx = x + d[i][0], yy = y + d[i][1];
        if (can(xx, yy) && a[xx][yy] < a[x][y]) {
            dfs(xx, yy);
            if (dp[xx][yy] + 1 > dp[x][y]) dp[x][y] = dp[xx][yy] + 1;
        }
    }
}
void work() {
    memset(dp, 0, sizeof(dp));
    for (int i = 0; i < r; ++i) {
        for (int j = 0; j < c; ++j) {
            scanf("%d", &a[i][j]);
        }
    }
    int ans = 0;
    for (int i = 0; i < r; ++i) {
        for (int j = 0; j < c; ++j) {
            dfs(i, j);
            ans = max(ans, dp[i][j]);
        }
    }
    printf("%d\n", ans);
}
int main() {
    while (scanf("%d%d", &r, &c) != EOF) work();
    return 0;
}


最后吐槽一下,tyvj 的评测机真的是............遥想去年好像就是卡了一面的评测都没耐心去看过没过(???)导致一直留到了今天

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值