smart_stupid 2024-04-08 15:05 采纳率: 0%
浏览 2

关于一道 AtCoder Beginner Contest 的题的一个疑问:一个不知道原理的算法

关于一道 AtCoder Beginner Contest 的题的一个疑问

我在补一道 ABC 的题 的时候交了一发代码,思路是先求出由药,起点和终点构成的小图,再在小图上判断可不可以达到终点。由于判断时用了最朴素的搜索,超时了,于是加上了一个优化,维护到达每一个点的最大能量,如果当前方案不是最优方案,则舍弃。但是在实现时将维护最大变成了维护最小值,但是通过了,改回来后样例都不是对的,求解释。

题目:

有一个网格,网格中有 $H$ 行和 $W$ 列。让 $(i, j)$ 表示从上往下第 $i$ 行,从左往上第 $j$ 列的单元格。每个单元格的状态由字符 $A_{i,j}$ 表示,其含义如下:

  • .:空单元格。
  • #:一个障碍物。
  • S:空单元格和起点。
  • T:空单元格和目标点。

高桥可以通过消耗 $1$ 能量从当前单元格移动到垂直或水平相邻的空单元格。如果能量为 $0$ ,则无法移动,也无法离开网格。

网格中有 $N$ 种药物。 $i$ -th药品位于空格 $(R_i, C_i)$ 处,可以用来将能量 $E_i$ 。注意,能量并不一定会增加。他可以在当前格子中使用药物。使用过的药品会消失。

高桥以 $0$ 的能量从起点开始,并希望到达目标点。请判断这是否可行。

Code:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
#include <time.h>
using namespace std;
int h, w, a[300][300];
char m[300][300];
int n;
int dis[400][400];                //小图上两个点之间的距离
int x[400], y[400];                //小图上每一个点对应原来坐标系上的位置,起点1,终点2
int dx[4] = {0, 0, -1, 1}, dy[4] = {-1, 1, 0, 0};
void bfs(int idx) {                //求出距离
    int dist[300][300];
    memset(dist, 0x3f, sizeof(dist));
    dist[x[idx]][y[idx]] = 0;
    queue<int> q;
    q.push(x[idx]);
    q.push(y[idx]);
    while (q.size()) {
        int nowx = q.front();
        q.pop();
        int nowy = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            int nx = nowx + dx[i], ny = nowy + dy[i];
            if (nx >= 1 && nx <= h && ny >= 1 && ny <= w && dist[nx][ny] == 0x3f3f3f3f && m[nx][ny] != '#') {
                dist[nx][ny] = dist[nowx][nowy] + 1;
                q.push(nx);
                q.push(ny);
            }
        }
    }
    for (int i = 1; i <= n + 2; i++) {
        dis[idx][i] = dist[x[i]][y[i]];
    }
}
bool vis[300][300];
int dis1[400];
void dfs(const int u, int t) {    //在小图上操作
    if (t < dis1[u]) dis1[u] = t;
    else return;
    int p = x[u], q = y[u];
    vis[x[u]][q] = 1;
    if (p == x[2] && q == y[2]) {
        cout << "Yes";
        exit(0);
    }
    int tmp = a[p][q];
    if (t < a[p][q] && a[p][q]) {
        t = a[p][q], a[p][q] = 0;
    }
    for (int i = 1; i <= n + 2; i++) {
        if ((x[i] != p || y[i] != q) && !vis[x[i]][y[i]] && t >= dis[u][i]) {
            t -= dis[u][i];
            dfs(i, t);
            t += dis[u][i];
        }
    }
    a[p][q] = tmp;
    vis[p][q] = 0;
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> h >> w;
    for (int i = 1; i <= h; i++) {
        for (int j = 1; j <= w; j++) {
            cin >> m[i][j];
            if (m[i][j] == 'S') {
                x[1] = i, y[1] = j;
            }
            if (m[i][j] == 'T') {
                x[2] = i, y[2] = j;
            }
        }
    }
    cin >> n;
    for (int i = 1; i <= n; i++) {
        int r, c, e;
        cin >> r >> c >> e;
        a[r][c] = e;
        x[i + 2] = r, y[i + 2] = c;
    }
    for (int i = 1; i <= n * 2; i++) {
        bfs(i);
    }
    memset(dis1, 0x3f, sizeof(dis1));
    dfs(1, 0);
    cout << "No";
    return 0;
}
  • 写回答

5条回答 默认 最新

  • Kwan的解忧杂货铺 Java领域优质创作者 2024-04-08 15:05
    关注

    下午好🌅🌅🌅
    本答案参考ChatGPT-3.5

    问题描述:关于一道 AtCoder Beginner Contest 的题的一个疑问

    回答:主要问题出在代码的dfs函数中,维护最大能量还是最小能量的问题。

    具体来说,函数dfs(u, t)意为在小图上从起点u出发,当前剩余能量为t的情况下,能否到达终点。在dfs的过程中,采取了剪枝优化的策略(如果当前方案没有达到终点的最优解,则剪枝)。

    按照代码实现,应该维护的是从起点到每一个结点的最小花费。因为dfs的本意是在小图上寻找一条花费最小的路径。但是在代码实现dfs的过程中,却将这个策略变成了维护最大能量。因此,将维护最大改成维护最小才能得到正确的答案。

    解决方案:

    • 修改dfs函数,将最大改成最小,保证正确的最优解被保留。

    修改后的代码如下:

    评论 编辑记录

报告相同问题?

问题事件

  • 修改了问题 4月8日
  • 创建了问题 4月8日

悬赏问题

  • ¥60 ESP32怎么烧录自启动程序
  • ¥50 html2canvas超出滚动条不显示
  • ¥15 java业务性能问题求解(sql,业务设计相关)
  • ¥15 52810 尾椎c三个a 写蓝牙地址
  • ¥15 elmos524.33 eeprom的读写问题
  • ¥15 使用Java milo连接Kepserver服务端报错?
  • ¥15 用ADS设计一款的射频功率放大器
  • ¥15 怎么求交点连线的理论解?
  • ¥20 软件开发方法学习来了
  • ¥15 微信小程序商城如何实现多商户收款 平台分润抽成