2024年3月28號題解

lwj1239發表於2024-03-29

Key Task

解題思路

  1. 分層圖最短路演算法
  2. 把圖中的節點和狀態當作一個節點而不是原圖中的點和勝利大逃亡(續)是一個模型,然後因為是求最短路所以使用bfs演算法來解決

程式碼實現

#define  _CRT_SECURE_NO_WARNINGS
#include <sstream>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>
#include<queue>
#include <set>
#define sc scanf
#define pr printf

using namespace std;

const int N = 101;//最多100個點

struct p {//佇列中存放的狀態,即真正遍歷的點,需要再加一個狀態
        int x;//行座標
        int y;//列座標
        int status;//得到了幾把鑰匙,二進位制位上的1表示有對應的鑰匙,0代表沒有對應的鑰匙
};

int h, c;//有幾行幾列
char s[N][N];//用來存放原始的圖
int sx;//起點的行座標
int sy;//起點的列座標
bool v[N][N][(1 << 4) + 1];//標記有那些狀態訪問過
//N * N是原始圖的狀態但還有鑰匙的狀態一共有4把鑰匙所以最多有2^4中可能,那麼到每個位置就是N * N * 2 ^ 4中可能
p q[N * N * ((1 << 4) + 1)];//佇列中最多容納這些狀態
int d[4][2] = {//上下左右走
        {-1, 0}, {1, 0}, {0, -1}, {0, 1}
};
int step[256];//存放鑰匙對應的在變數(status)中的二進位制位數

void solve() {
        memset(v, 0, sizeof(v));//把v陣列全部賦值為0
        for (int i = 0; i < h; i++) {//讀取h行
                sc("%s", s[i]);
                //找到起點的位置
                for (int j = 0; j < c; j++) {
                        if (s[i][j] == '*') {
                                sx = i;
                                sy = j;
                                break;
                        }
                }
        }
        //佇列頭和佇列尾初始化為0代表沒有佇列中沒有元素
        int l = 0;
        int r = 0;
        p start = { sx, sy, 0 };//起點的狀態剛開始沒有鑰匙,所以status賦值為0
        int level = 0;//第幾層,層數就是我們走的路徑即秒數

        v[sx][sy][0] = 1;//起點狀態標記為1表示走過
        q[r++] = start;//起點狀態入隊進行bfs

        while (l < r) {//只要佇列中還有元素
                int size = r - l;//取出當前層數的節點個數,進行層序遍歷

                for (int i = 0; i < size; i++) {//取出當前層的每一個節點
                        start = q[l++];//出隊

                        if (s[start.x][start.y] == 'X') {//如果到達終點了
                                pr("Escape possible in %d steps.\n", level);//列印需要的時間
                                return;//直接結束bfs因為第一次就是最短路徑
                        }

                        for (int i = 0; i < 4; i++) {//遍歷四個方向
                                //新的座標
                                int nx = start.x + d[i][0];
                                int ny = start.y + d[i][1];
                                //如果沒有越界並且這個狀態沒有被訪問過並且還不是牆
                                if (nx >= 0 && nx < h && ny >= 0 && ny < c && !v[nx][ny][start.status] && s[nx][ny] != '#') {
                                        p t = start;//因為不能改變start,所以用一個臨時變數存起來
                                        if (s[nx][ny] == 'b' || s[nx][ny] == 'y' || s[nx][ny] == 'r' || s[nx][ny] == 'g') {//如果是一把鑰匙
                                                //更新一下狀態
                                                t.x = nx;
                                                t.y = ny;
                                                t.status |= 1 << step[s[nx][ny]];//放入對應的位置
                                                v[nx][ny][t.status] = 1;//把狀態標記為訪問過
                                                q[r++] = t;//入隊
                                        }
                                        else if ((s[nx][ny] == 'B' || s[nx][ny] == 'Y' || s[nx][ny] == 'R' || s[nx][ny] == 'G')) {//碰到門了
                                                if (((t.status >> step[s[nx][ny]]) & 1)) {//如果有對應的鑰匙那麼就可以走,把這個if寫在上面
                                                    //因為沒有對應的鑰匙就不能走那麼它就執行else了就入隊了,就不對了
                                                        //更新可以走的狀態
                                                        t.x = nx;
                                                        t.y = ny;
                                                        v[nx][ny][t.status] = 1;
                                                        q[r++] = t;//入隊
                                                }       
                                        }
                                        else {//如果不是鑰匙也不是門,那麼只有一種可能就是空地,那麼直接入隊
                                                t.x = nx;
                                                t.y = ny;
                                                t.status = start.status;
                                                v[nx][ny][t.status] = 1;
                                                q[r++] = t;
                                        }
                                }
                        }
                }
                level++;//層序遍歷,所以結束的時候層數加一
        }
        pr("The poor student is trapped!\n");//佇列為空還沒有搜尋到出口代表出不去所以列印出不去
}

int main() {
        step['B'] = 0;//b鑰匙在最低位
        step['Y'] = 1;//y鑰匙在第一位
        step['R'] = 2;//r鑰匙在第二位
        step['G'] = 3;//g鑰匙在第三位
        step['b'] = 0;//b鑰匙在最低位
        step['y'] = 1;//y鑰匙在第一位
        step['r'] = 2;//r鑰匙在第二位
        step['g'] = 3;//g鑰匙在第三位
        while (~sc("%d%d", &h, &c), h + c) {//讀取行和列,如果h+c的值為0,代表h和c的值都為0
                solve();
        }

        return 0;
} 

Beat

解題思路

  1. dfs把所有的可能都搜尋出來,那麼只要維護一個變數就可以了,然後列印最大值
  2. dfs列舉所有的問題,看滿不滿足難度是遞增的,如果滿足繼續遞迴呼叫

程式碼實現

#define  _CRT_SECURE_NO_WARNINGS
#include <sstream>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>
#include<queue>
#include <set>
#define sc scanf
#define pr printf

using namespace std;

const int N = 16;//最多16種問題

int n;//問題的個數
int a[N][N];//第ij位置表示解決第i個問題後解決第j問題需要的時間
bool v[N];//用來標記解決過的問題
int ans;//最多可以解決多少個問題

void dfs(int pre, int m, int cnt)//pre表示上一個解決的問題,m表示上一個解決的問題的難度,cnt表示解決問題的個數
{
        if (cnt > ans) {//每次都更新答案
                ans = cnt;
        }
        for (int i = 2; i <= n; i++) {//遍歷所有的問題,因為第一個問題是第一次就解決的所以從第二個問題開始
                if (!v[i] && a[pre][i] >= m) {//如果這個問題沒有被解決,並且滿足難度越來越高的條件
                        v[i] = 1;//標記已經解決
                        dfs(i, a[pre][i], cnt + 1);//解決了i問題所以是i,而難度是a[pre][i],解決問題的個數要加一
                        v[i] = 0;//恢復現場
                }
        }
}

int main() {
        while (~sc("%d", &n))
        {
                ans = 0;
                for (int i = 1; i <= n; i++) {
                        for (int j = 1; j <= n; j++) {
                                sc("%d", a[i] + j);
                        }
                }
                memset(v, 0, sizeof(v));
                v[1] = 1;
                ans = 1;
                dfs(1, 0, 1);

                pr("%d\n", ans);
        }

        return 0;
}