Key Task
解題思路
- 分層圖最短路演算法
- 把圖中的節點和狀態當作一個節點而不是原圖中的點和勝利大逃亡(續)是一個模型,然後因為是求最短路所以使用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
解題思路
- dfs把所有的可能都搜尋出來,那麼只要維護一個變數就可以了,然後列印最大值
- 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;
}