目录
前言
字符数字的转换
bfs or double dfs
棋局的编号
Solitaire
问题描述
输入
输出
问题分析
判重
棋子走动逻辑
单向搜索代码
双向搜索退出条件
双向广搜代码
前言
交代一下我写这题的感受,被自己气笑了,本来以为是我字符串没弄好,就总盯着字符串,结果发现连最基本的变量赋值逻辑都弄错了,服了
字符数字的转换
字符-'0' 字符变数字,数字+'0' 数字变字符
bfs or double dfs
这题为什么不能用bfs,其实用bfs还是用double bfs很好判断,就看一下每次入队的状态数量为多少即可
本题总共有四个棋子,每个棋子都有上下左右四个方向,就意味着单个节点入队最多为4*4=16,种方案,不难发现,这个还是以指数形增长的,如果连续八步那么就会拓展出,>1500万种状态,非常恐怖。
所以要用double bfs减少状态数大约2*=131072种棋局,具体关于double bfs分析在这里双向广搜
棋局的编号
本题为了方便判重,我对该棋盘的所有格子都进行了编号
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
57 | 58 | 59 | 60 | 61 | 62 | 64 | 64 |
例如四个棋子如果为(1,1),(1,2),(1,3),(1,4)那么我就用string s="1234"代表它
Solitaire
问题描述
在一个8*8的棋盘中放入四个棋子,要求八步之内四颗棋子到达指定位置,现给出四颗棋子的初始位置,和最终位置,判断是否可以到达终点
输入
总共两行,第一行用八个整数代表四颗棋子的初始位置,第二行用八个整数代表四颗棋子的终止位置
输出
如果可以到达终点,那么输出"YES",否则输出"NO"
问题分析
前面已经说过,本题需要用到双向广搜,我们不妨先实现单向搜索
判重
其实这才是我想发这题的题解的原因,本题最重要的就是判重的操作,首先明确一点:绝对不能用int vis[][][][][][][][]判重,这个八位数组占用空间太大,算一下等于=104.8576GB,会爆掉
所以次优先的方法就是改用char数组那么就可以减到=16MB,勉强可以接受。但如果把这个八位数砍成一个字符串,用map压缩无效空间,那么这个内存占用将会减少到几kb
棋子走动逻辑
题目说棋子可以上下左右移动,如果碰到该方向有棋子,那么就往该方向再移动一格(这个操作最多一次),根据代码逻辑就可以编写代码
nx += dir[k][0];
ny += dir[k][1];
if (!check(nx, ny, i,now.s)) {
nx +=dir[k][0];
ny +=dir[k][1];
if (!check(nx, ny, i,now.s)) { //如果还有棋子,continue
continue;
}
}
check函数:
//这个字符串索引逻辑可以参考上图
bool check(int x,int y,int i,string s) {
for(int k=0;k<4;k++){
if (k != i) {
if (x == (s[2*k]-'0') && y == (s[2*k + 1]-'0') ) {
return false;
}
}
}
return true;
}
translate函数
这个是将棋子位置转化为一个独一无二的字符串(方便判重)的代码
string Translate(string s) {
vector<int> pos(4);
for (int i = 0; i < 4; i++) {
// 将每两位字符转换为一个十进制数
// 注意:这里假设输入字符串只包含'0'和'1',且长度至少为8
pos[i] = ((s[2 * i] - '0')-1) * 8 + (s[2 * i + 1] - '0');
}
sort(pos.begin(), pos.end());
string a;
for (int i = 0; i < 4; i++) {
// 将排序后的十进制数转换回字符串,并拼接到结果字符串中
a += to_string(pos[i]);
}
return a;
}
单向搜索代码
#include<string>
#include<iostream>
#include<map>
#include<queue>
#include<algorithm>
using namespace std;
int dir[4][2] = { {0,-1},{0,1},{1,0},{-1,0} };
struct node {
string s;
int step;
node() {}
node(string ss,int ste):s(ss),step(ste){}
};
map<string, bool>vis;
queue<node>que;
int pos[4];
string m;
string st, ed;
string ed_s;
string st_s;
node now, nxt;
bool flag=false;
string Translate(string s) {
vector<int> pos(4);
for (int i = 0; i < 4; i++) {
// 将每两位字符转换为一个十进制数
// 注意:这里假设输入字符串只包含'0'和'1',且长度至少为8
pos[i] = ((s[2 * i] - '0')-1) * 8 + (s[2 * i + 1] - '0');
}
sort(pos.begin(), pos.end());
string a;
for (int i = 0; i < 4; i++) {
// 将排序后的十进制数转换回字符串,并拼接到结果字符串中
a += to_string(pos[i]);
}
return a;
}
bool check(int x,int y,int i,string s) {
for(int k=0;k<4;k++){
if (k != i) {
if (x == (s[2*k]-'0') && y == (s[2*k + 1]-'0') ) {
return false;
}
}
}
return true;
}
void bfs() {
que.push(node(st, 0));
m=Translate(st);
vis[m] = true;
while (!que.empty()) {
now = que.front();
que.pop();
for(int i=0;i<4;i++){
//一开始把这行放这了,我是真的服了
//int nx = int(now.s[2*i] - '0'), ny = int(now.s[2*i + 1] - '0');
for (int k = 0; k < 4; k++) {
int nx = int(now.s[2*i] - '0'), ny = int(now.s[2*i + 1] - '0');
nx += dir[k][0];
ny += dir[k][1];
if (!check(nx, ny, i,now.s)) {
nx +=dir[k][0];
ny +=dir[k][1];
if (!check(nx, ny, i,now.s)) {
continue;
}
}
if (nx < 1 || nx > 8 || ny < 1 || ny > 8) continue;
nxt.s = now.s;
nxt.s[2*i] = '0'+nx;
nxt.s[2*i + 1] = '0'+ny;
nxt.step = now.step + 1;
if (nxt.step > 8) continue;
m = Translate(nxt.s);
if (nxt.step <= 8 && m == ed_s) {
flag = true;
return;
}
if (!vis[m]) {
vis[m] = true;
que.push(nxt);
}
}
}
}
}
//注:字符-'0' 字符变数字,数字+'0' 数字变字符
int main() {
int i, j;
for (int k = 1; k <= 8; k++) {
cin >> i;
st += '0'+i;
}
for (int k = 1; k <= 8; k++) {
cin >> j;
ed += '0'+j;
}
st_s = Translate(st);
ed_s = Translate(ed);
if (st_s == ed_s) {
cout << "YES" << endl;
return 0;
}
bfs();
if (flag) {
cout << "YES" << endl;
}
else {
cout << "NO" << endl;
}
return 0;
}
接下来我们就把它改为双向搜索代码
双向搜索退出条件
双向搜索是起点和终点同时同速度出发搜索,那么他们相遇的时候所走的路程一定相同,要求总步数不大于8,那么每个人走的步数也要不大于4,此外我们定义了一个visa判重1代表起点搜过,2代表终点搜过,如果每一个队列搜到了另一个队列的标记,那么也代表他们相遇了
顺嘴提一句,那个if (nxt.step > 8) continue;的剪枝8也要改成4
双向广搜代码
#include<string>
#include<iostream>
#include<map>
#include<queue>
#include<algorithm>
using namespace std;
int dir[4][2] = { {0,-1},{0,1},{1,0},{-1,0} };
struct node {
string s;
int step;
node() {}
node(string ss,int ste):s(ss),step(ste){}
};
map<string, int>vis;
queue<node>qa;
queue<node>qb;
int pos[4];
string m;
string st, ed;
string ed_s;
string st_s;
node now, nxt;
bool flag=false;
string Translate(string s) {
vector<int> pos(4);
for (int i = 0; i < 4; i++) {
pos[i] = ((s[2 * i] - '0')-1) * 8 + (s[2 * i + 1] - '0');
}
sort(pos.begin(), pos.end());
string a;
for (int i = 0; i < 4; i++) {
// 将排序后的十进制数转换回字符串,并拼接到结果字符串中
a += to_string(pos[i]);
}
return a;
}
bool check(int x,int y,int i,string s) {
for(int k=0;k<4;k++){
if (k != i) {
if (x == (s[2*k]-'0') && y == (s[2*k + 1]-'0') ) {
return false;
}
}
}
return true;
}
void dul_bfs() {
qa.push(node(st, 0));
qb.push(node(ed, 0));
vis[st_s] = 1;
vis[ed_s] = 2;
while (!qa.empty() && !qb.empty()) {
if (qa.size() < qb.size()) {
now = qa.front();
qa.pop();
for(int i=0;i<4;i++){
for (int k = 0; k < 4; k++) {
int nx = now.s[2*i] - '0', ny = now.s[2*i + 1] - '0';
nx += dir[k][0];
ny += dir[k][1];
if (!check(nx, ny, i,now.s)) {
nx +=dir[k][0];
ny +=dir[k][1];
if (!check(nx, ny, i,now.s)) {
continue;
}
}
if (nx < 1 || nx > 8 || ny < 1 || ny > 8) continue;
nxt.s = now.s;
nxt.s[2*i] = '0'+nx;
nxt.s[2*i + 1] = '0'+ny;
nxt.step = now.step + 1;
if (nxt.step > 4) continue;
m = Translate(nxt.s);
if (nxt.step <= 4 && vis[m]==2) {
flag = true;
return;
}
if (!vis[m]) {
vis[m] = 1;
qa.push(nxt);
}
}
}
}
else {
now = qb.front();
qb.pop();
for (int i = 0; i < 4; i++) {
for (int k = 0; k < 4; k++) {
int nx = now.s[2 * i] - '0', ny = now.s[2 * i + 1] - '0';
nx += dir[k][0];
ny += dir[k][1];
if (!check(nx, ny, i, now.s)) {
nx += dir[k][0];
ny += dir[k][1];
if (!check(nx, ny, i, now.s)) {
continue;
}
}
if (nx < 1 || nx > 8 || ny < 1 || ny > 8) continue;
nxt.s = now.s;
nxt.s[2 * i] = '0' + nx;
nxt.s[2 * i + 1] = '0' + ny;
nxt.step = now.step + 1;
if (nxt.step > 4) continue;
m = Translate(nxt.s);
if (nxt.step <= 4 && vis[m]==1) {
flag = true;
return;
}
if (!vis[m]) {
vis[m] = 2;
qb.push(nxt);
}
}
}
}
}
}
//注:字符-'0' 字符变数字,数字+'0' 数字变字符
int main() {
int i, j;
for (int k = 1; k <= 8; k++) {
cin >> i;
st += '0'+i;
}
for (int k = 1; k <= 8; k++) {
cin >> j;
ed += '0'+j;
}
st_s = Translate(st);
ed_s = Translate(ed);
if (st_s == ed_s) {
cout << "YES" << endl;
return 0;
}
dul_bfs();
if (flag) {
cout << "YES" << endl;
}
else {
cout << "NO" << endl;
}
return 0;
}