P3956 [NOIP2017 普及组] 棋盘

暴力bfs


/*
    这题本身不难, 但是我写难了
    就是一个bfs, 没了
    但是我的写法恰好犯了一个错误
    hark数据
    3 5
    1 1 0
    2 1 1
    3 1 1
    2 2 0
    3 3 0
    答案是4而我能输出3
     0 -1 -1 
     1  0 -1 
     1 -1  0 
    原因是 我先走到了(2, 2)这时候到(3, 2)的花费最小是4, 我还加入了队列, 这时候的上一个颜色是0, 这个记为a
    然后, 我后面又走到了(3, 1)从这个到(3, 2) 更新了最小花费为3, 我也加入了队列, 这时候的上一个颜色是1, 这个记为b
    但是前面花费是4的时候我加入了队列(a), 上一个颜色是0, 导致走到(3, 3)时上个颜色是0 和 (3, 3)的0相等不花费
    就把到(3, 3)的最小花费变成了3, 就错了, 
    使用优先队列, 用到每个点的最小距离排, 就可以解决这个问题, 就可以先算b, 把正确答案算出来
    
    这里说一下优先队列的结构体排序
    众所周知优先队列默认是大根堆
    是从大到小排, 但是是用的小于号, 这时候直接重载小于号像下面就行了, 不用更换模式
*/
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 1100, M = 110;

int g[M][M];
int n, m;
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
// bool st[M][M];
int dist[M][M];

struct Node
{
    int sum;
    int x, y;
    bool flag; // 到这一步是否使用了魔法
    int now; // now是当前的颜色, 因为不能更改嘛, 用这个记录一下这次的颜色
    bool operator< (const Node &W)const
    {
        return sum < W.sum;
    }
};

int bfs()
{
    memset(dist, 0x3f, sizeof dist);
    priority_queue<Node> q;
    q.push({0, 1, 1, 0, g[1][1]});
    dist[1][1] = 0;
    
    while (q.size())
    {
        auto t = q.top();
        q.pop();
        
        int x = t.x, y = t.y, now = t.now;
        bool flag = t.flag;
        
        for (int i = 0; i < 4; i ++ )
        {
            int a = x + dx[i], b = y + dy[i], s = 0; // s是这次走的花费
            if (a < 1 || b < 1 || a > n || b > n) continue;
            if (g[a][b] != now) // 如果颜色不同
            {
                if (g[a][b] >= 0) s = 1; // 这个点有颜色
                else if (!flag) s = 2; // 没颜色且之前没用过魔法
                else continue; // 说明已经到不了了
            }
            
            if (dist[a][b] > dist[x][y] + s)
            {
                dist[a][b] = dist[x][y] + s;
                // if (a == 4 && b == 4) cout << x << ' ' << y << ' ' << now << endl;
                // cout << a << ' ' << b << endl;
                q.push({dist[a][b], a, b, s == 2, s == 2 ? now : g[a][b]}); // 这段话可以细品 s == 2 ? now : g[a][b]是如果使用了魔法, 那么这个点的颜色就是上个点的颜色, 没有的话就是它自身的颜色
            }
        }
    }
    
    return dist[n][n];
}

int main()
{
    memset(g, -1, sizeof g);
    cin >> n >> m;
    while (m -- )
    {
        int a, b, c;
        cin >> a >> b >> c;
        g[a][b] = c + 6;
    }
    // for (int i = 1; i <= n; i ++ )
    // {
    //     for (int j = 1; j <= n; j ++ )
    //         printf("%2d ", g[i][j]);
    //     puts("");
    // }
            
    int t = bfs();
    if (t != 0x3f3f3f3f) cout << t;
    else cout << -1;
    
    return 0;
}


// 错误的写法

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 1100, M = 110;

int g[M][M];
int n, m;
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
// bool st[M][M];
int dist[M][M];

struct Node
{
    int x, y;
    bool flag;
    int now;
};

int bfs()
{
    memset(dist, 0x3f, sizeof dist);
    queue<Node> q;
    q.push({1, 1, 0, g[1][1]});
    dist[1][1] = 0;
    
    while (q.size())
    {
        auto t = q.front();
        q.pop();
        
        int x = t.x, y = t.y, now = t.now;
        bool flag = t.flag;
        
        for (int i = 0; i < 4; i ++ )
        {
            int a = x + dx[i], b = y + dy[i], s = 0;
            if (a < 1 || b < 1 || a > n || b > n) continue;
            if (g[a][b] != now)
            {
                if (g[a][b] >= 0) s = 1;
                else if (!flag) s = 2;
                else continue; // 说明已经到不了了
            }
            
            if (dist[a][b] > dist[x][y] + s)
            {
                dist[a][b] = dist[x][y] + s;
                if (a == 4 && b == 4) printf("%d %d %d %d %d %d\n", x, y, s, g[a][b], g[x][y], now);
                // cout << a << ' ' << b << endl;
                q.push({a, b, s == 2, (s == 2) ? now : g[a][b]});
            }
        }
    }
    
    return dist[n][n];
}

int main()
{
    memset(g, -1, sizeof g);
    cin >> n >> m;
    while (m -- )
    {
        int a, b, c;
        cin >> a >> b >> c;
        g[a][b] = c;
    }
    
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= n; j ++ )
            printf("%2d ", g[i][j]);
        puts("");
    }
            
    int t = bfs();
    if (t != 0x3f3f3f3f) cout << t;
    else cout << -1;
    
    return 0;
}

记忆化搜索

#include <iostream>
#include <cstring>
#include <algorithm>
#include <noip2024rp++>

using namespace std;

const int N = 110, M = 1010;

int g[N][N];
int n, m;
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
int dist[N][N];

void dfs(int x, int y, bool flag, int c, int sum) 
{
    if (dist[x][y] <= sum) return;
    dist[x][y] = sum;
    
    for (int i = 0; i < 4; i ++ )
    {
        int a = x + dx[i], b = y + dy[i], cnt = 0;
        if (a < 1 || b < 1 || a > n || b > n) continue;
        if (g[a][b] != c)
        {
            if (g[a][b] >= 0) cnt = 1;
            else if (!flag) cnt = 2;
            else continue;
        }
        dfs(a, b, cnt == 2, cnt == 2 ? c : g[a][b], sum + cnt);
    }
}

int main()
{
    cin >> n >> m;
    
    memset(g, -1, sizeof g);
    while (m -- )
    {
        int x, y;
        cin >> x >> y >> g[x][y];
    }
    
    memset(dist, 0x3f, sizeof dist);
    dfs(1, 1, 0, g[1][1], 0);
    
    int ans = dist[n][n];
    if (ans < 0x3f3f3f3f / 2) cout << ans << endl;
    else cout << -1 << endl;
    
    return 0;
}
void dfs(int x, int y, bool flag, int c)
{
    for (int i = 0; i < 4; i ++ )
    {
        int a = x + dx[i], b = y + dy[i], cnt = 0;
        if (a < 1 || b < 1 || a > n || b > n) continue;
        if (g[a][b] != c)
        {
            if (g[a][b] >= 0) cnt = 1;
            else if (!flag) cnt = 2;
            else continue;
        }
        if (dist[a][b] > dist[x][y] + cnt)
        {
            dist[a][b] = dist[x][y] + cnt;
            dfs(a, b, cnt == 2, cnt == 2 ? c : g[a][b]);
        }
    }
}

posted @ 2024-04-14 15:25  blind5883  阅读(26)  评论(0编辑  收藏  举报