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;
}