(新版)SJTU-OJ-1037. 小可怜操纵大选

题目描述

助教是个小可怜,因为她不知道要出什么机考题了。然后她发现大家上周学了搜索,欣喜若狂。那一天刚好是大选之日,她在搜索引擎输入了election 2020,开始关注自己还能不能出国读书。选情十分焦灼,小可怜越看越紧张,一怒之下关闭网页开始睡回笼觉。小可怜希望在梦中能获得操纵大选的能力。

日有所思,日有所梦。小可怜梦到自己来到了一个国家USB(United States of Bairimeng),这个国家由 m − 1 m - 1 m1条经线和 m − 1 m - 1 m1条纬线分隔,一共拥有 m × m m \times m m×m个州,每个州是红州,蓝州,摇摆州中的一种。一位叫做Truden的人想要找一条路,从USB最西北角的州走到最东南角的州。

任何一个时刻,Truden不被允许出现在摇摆州。Truden只能往上下左右四个方向的州前进,当Truden从一个州走到另一个州,如果两个州的颜色相同,那么Truden不需要支付过路费,否则Truden需要支付1USB元的过路费。

但不能取道摇摆州使Truden十分苦恼,于是Truden找上了在USB做大法师的小可怜。每次只要支付2USB元,小可怜就会帮Truden暂时把一个摇摆州变成红州或者蓝州中的一种。但是要注意的是,小可怜使用魔法后,Truden可以入境一个被变色的摇摆州,但当Truden还在此州境内时,小可怜不会帮Truden把另一个摇摆州也变色,直到Truden离开此州,小可怜才同意会帮Truden再次施法。而当Truden离开了变色了的摇摆州后,这个州又将变成摇摆州。

请你帮Truden计算一下,他最少要花费多少USB元,才能从西北角的州走到东南角的州。

输入格式

第一行包含两个正整数 m , n m,n m,n,以一个空格分开,分别代表USB拥有 m × m m \times m m×m个州,以及摇摆州的数量是 n n n

接下来的nn行,每行三个整数 x , y , c x,y,c x,y,c,相邻的两个整数由一个空格隔开,分别表示坐标为 ( x , y ) (x,y) (x,y)的州的立场。坐标为 ( x , y ) (x,y) (x,y)的州表示,从北往南数第 x x x行,从西往东数第 y y y列的那个州。如果 c = 1 c = 1 c=1,此州是蓝州, c = 0 c = 0 c=0,此州是红州。

而其他的州均为摇摆州。数据保证西北角的州(即坐标为 ( 1 , 1 ) (1,1) (1,1)的州)不是摇摆州。

输出格式

一个整数,表示花费的USB元的最小值。如果Truden无法到达东南角的州,请输出-1。

样例输入

样例1
5 7
1 1 0
1 2 0
2 2 1
3 3 1
3 4 0
4 4 1
5 5 0
样例2
5 5
1 1 0
1 2 0
2 2 1
3 3 1
5 5 0

样例输出

样例1
8
样例2
-1

数据范围

对于 30 % 30\% 30%的数据, 1 ≤ m ≤ 5 , 1 ≤ n ≤ 10 1 \leq m \leq 5,1 \leq n \leq 10 1m5,1n10

对于 60 % 60\% 60%的数据, 1 ≤ m ≤ 20 , 1 ≤ n ≤ 200 1 \leq m \leq 20,1\leq n \leq 200 1m20,1n200

对于 100 % 100\% 100%的数据, 1 ≤ m ≤ 100 , 1 ≤ n ≤ 4 , 000 1 \leq m \leq 100,1 \leq n \leq 4,000 1m100,1n4,000

小可怜眼看各州转红,一怒之下把全国都变成蓝色的了!(此行为不影响本题,请按题面做题)

题目彩蛋

       白日梦帝国你发现了吗?
       Truden你发现啦吗?Trump + Baiden

题目答疑

       首先,题目明确说了,这是一道搜索的题目,那还是回到我们之前所学的广度优先搜素和深度优先搜索。我们来看看可能遇到的问题。

  • 问题一:这道题到底是用BFS还是用DFS?
  • 解答:首先实践证明【DFS】、【BFS】均可以完成这道题目!证明如下:显然你已经猜出来哪个时间是【DFS】哪个是【BFS】的结果。
评测编号用户题目名称评测状态运行时间内存语言提交时间
49729Musicminion1037. 小可怜操纵大选Accepted98ms32248KiBC++Jul-28-2021 14:18:49
49728Musicminion1037. 小可怜操纵大选Accepted710ms33000KiBC++Jul-28-2021 13:07:43

个人认为,只要[BFS]可以做的题目,[DFS]一定可以做,反之亦然。我们还是从图的角度看看。下图是DFS函数搜索的时候函数递归的运行情况,那么,如果DFS可以的话,我们可以假设一个队列,把第二层函数要递归的全部放入队列,然后一次按照队列执行,第三层同理,这样我们就把[DFS]变成了[BFS],而且优化了时间。
请添加图片描述

  • 问题二:如何选择BFS还是用DFS?
  • 解答:第一,取决于你会不会队列。(这个划掉,不过如果刚刚学习程序设计的同学可以优先考虑DFS)毕竟按照教材内容【程序设计思想与方法(翁慧玉)】的编排,确实先学习的函数递归,队列的知识要到数据结构才会学习,不过个人认为队列的知识可以自己学会,参考之前我的文章【DFS】【队】知识的传送门;第二,取决于搜索的选择,比如想这种,每次可以走的只有周围四个方向,完全是可以BFS的,但是,如果每次可以走的方向非常多【我们假设】,多到可能会把这个队列撑爆了,那显然不可以BFS。
  • 问题三:BFS、DFS的核心是什么?
  • 解答:是防止重复搜索,或者说陷入死循环。有下面几种可能方法,
    【方法一】我们开一个 bool 数组,一边搜索,只要搜索到就做好标记,每次搜索前检测是否之前已经搜索过。
    【方法二】不开 bool 数组,利用数据的比较,开始的时候把用来记录 关键信息 [例如步数、花费的钱] 的数组全部初始化为 INT_MAX [注意,此时不要对这个数组的元素进行加法或者其他运算,会爆INT的!],然后每次只要输入的数据比之前的小,就允许输入。或者你初始化为一个很大值也可以。
    【比较】两种方法显然第二种更好,因为 如果从其他路径搜索,搜索到了之前已经搜索的点,但是发现有更优解,这个时候方法二可以更新之前的结果!

题目解答

【DFS】的解答:

#include <iostream>
using namespace std;
int dx[]={0,-1,0,1};
int dy[]={1,0,-1,0};
int m,n;
int ans = 99999;
int x, y, c;
int path[101][101];
int best[101][101];
void dfs(int sx, int sy ,int flag , int price)
{
    if (sx == m && sy ==m)
    {
        ans = min(ans, price);
        best[m][m] = price;
        return;
    }
    if (price>=best[sx][sy])
        return;

    best[sx][sy]=price;

    for(int i=0 ;i<4 ;i++)
    {
        int tx = sx + dx[i];
        int ty = sy + dy[i];
        if (tx < 1 || ty < 1 || tx > m || ty > m)
        {
            continue;
        }

        if (path[tx][ty])       // 目前去的不是摇摆州
        {
            if (path[tx][ty] == path[sx][sy])
                dfs(tx, ty, false, price);              
            else
                dfs(tx, ty, false, price + 1);
        } 
        else if (!flag)         
        // 目前去的是摇摆州 flag表示当前是否在摇摆州,!flag表示当前不在摇摆州
        {
            path[tx][ty]= path[sx][sy];
            dfs(tx, ty, true, price + 2);
            path[tx][ty] = false;
        }
    }
}

int main()
{
    cin>>m>>n;
    for(int i=1 ;i<=m;i++)
    {
        for(int j=1 ;j<=m;j++)
        {
            best[i][j]=999;
        }
    }
    for(int i=1;i<=n;i++)
    {
        cin>>x>>y>>c;
        path[x][y] = c+1;
    }
    dfs(1,1, false,0);
    if (ans == 99999)
        cout << -1 << endl;
    else
        cout << ans << endl;

    for (int i = 1; i <= m; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            cout << path[i][j] << "\t";
        }
        cout << endl;
    }

    cout << endl;
    for (int i = 1; i <= m; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            cout << best[i][j] << "\t";
        }
        cout << endl;
    }

    system("pause");
    return 0;
}

【BFS】的解答:其实我是根据DFS的改编的

#include <iostream>
#include <queue>
using namespace std;

class node
{
    public: 
    int x;
    int y;
    bool ifSwing;
    int price;
    int lastStatus;
};

int dx[4] = {0, -1, 0, 1};
int dy[4] = {1, 0, -1, 0};
int m,n;
int ans = 99999;
int x, y, c;
int path[101][101];
int best[101][101];
node departure, destination;

void bfs(node departure)
{
    node np, nxp;
    queue<node> bfsque;
    bfsque.push(departure);

    while(!bfsque.empty())
    {
        np = bfsque.front();
        bfsque.pop();
        if (np.price >= best[np.x][np.y])
        {
            continue;
        }
        else
            best[np.x][np.y] = np.price;
        for (int i = 0; i < 4; i++)
        {
            nxp.x = np.x + dx[i];
            nxp.y = np.y + dy[i];
            if (nxp.x < 1 || nxp.x > m || nxp.y < 1 || nxp.y > m)
            {
                continue;
            }
            if (np.x == m && np.y == m)
            {
                ans = min(ans, np.price);
                best[m][m] = ans;
            }
            if (path[nxp.x][nxp.y] > 0 && np.ifSwing == false)
            {
                if (path[nxp.x][nxp.y] == path[np.x][np.y])
                {
                    nxp.ifSwing = false;
                    nxp.price = np.price;
                    nxp.lastStatus = 0;
                    bfsque.push(nxp);
                }
                else
                {
                    nxp.ifSwing = false;
                    nxp.price = np.price + 1;
                    nxp.lastStatus = 0;
                    bfsque.push(nxp);
                }
            }
            if (path[np.x][np.y] != 0 && path[nxp.x][nxp.y] == 0)
            {
                nxp.ifSwing = true;
                nxp.lastStatus = path[np.x][np.y];
                nxp.price = np.price + 2;
                bfsque.push(nxp);
            }

            if (path[nxp.x][nxp.y] > 0 && np.ifSwing == true)
            {
                nxp.ifSwing = false;
                if (np.lastStatus == path[nxp.x][nxp.y])
                    nxp.price = np.price;
                else
                    nxp.price = np.price + 1;
                
                nxp.lastStatus = 0;
                bfsque.push(nxp);
            }
        }
    }
}
int main()
{
    cin >> m >> n;

    for(int i=1 ;i<=m;i++)
    {
        for(int j=1 ;j<=m;j++)
        {
            best[i][j]=99999;
        }
    }

    for(int i=1;i<=n;i++)
    {
        cin >> x >> y >> c;
        path[x][y] = c + 1;
    }

    departure.x = 1;
    departure.y = 1;
    departure.ifSwing = false;
    departure.lastStatus = 0;
    departure.price = 0;
    bfs(departure);
    
    if (ans == 99999)
        cout << -1 << endl;
    else
        cout << ans << endl;
    system("pause");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值