华容道
Posted darlingroot
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了华容道相关的知识,希望对你有一定的参考价值。
(一时咕一时爽,一直咕一直爽
(大佬说30分很好拿
(大佬随随便便秒出的70分
(我这个蒟蒻苟的一批
-------------------------------------------------------------------------------------------------------
题目描述
小 B
最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时间。
小 B
玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的:
-
在一个 n×m 棋盘上有n×m个格子,其中有且只有一个格子是空白的,其余n×m−1个格子上每个格子上有一个棋子,每个棋子的大小都是 1×1 的;
-
有些棋子是固定的,有些棋子则是可以移动的;
-
任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。
游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。
给定一个棋盘,游戏可以玩 q 次,当然,每次棋盘上固定的格子是不会变的, 但是棋盘上空白的格子的初始位置、 指定的可移动的棋子的初始位置和目标位置却可能不同。第 i 次玩的时候, 空白的格子在第 EXi行第 EYi列,指定的可移动棋子的初始位置为第 SXi 行第 SYii?列,目标位置为第 TXi 行第 TYi? 列。
假设小 B
每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小 B
每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。
输入输出格式
输入格式:第一行有 3个整数,每两个整数之间用一个空格隔开,依次表示n,m,q ;
接下来的 n 行描述一个n×m 的棋盘,每行有m个整数,每两个整数之间用一个空格隔开,每个整数描述棋盘上一个格子的状态,0 表示该格子上的棋子是固定的,1 表示该格子上的棋子可以移动或者该格子是空白的。
接下来的 q 行,每行包含 6 个整数依次是 EXi,EYi,SXi,SYi,TXi,TYi,每两个整数之间用一个空格隔开,表示每次游戏空白格子的位置,指定棋子的初始位置和目标位置。
输出格式:共q 行,每行包含 1 个整数,表示每次游戏所需要的最少时间,如果某次游戏无法完成目标则输出−1。
输入输出样例
3 4 2 0 1 1 1 0 1 1 0 0 1 0 0 3 2 1 2 2 2 1 2 2 2 3 2
2 -1
说明
【输入输出样例说明】
棋盘上划叉的格子是固定的,红色格子是目标位置,圆圈表示棋子,其中绿色圆圈表示目标棋子。
- 第一次游戏,空白格子的初始位置是 (3,2)(图中空白所示),游戏的目标是将初始位置在(1,2)上的棋子(图中绿色圆圈所代表的棋子)移动到目标位置(2,2)(图中红色的格子)上。
移动过程如下:
-
第二次游戏,空白格子的初始位置是(1,2)(图中空白所示),游戏的目标是将初始位置在(2,2)上的棋子(图中绿色圆圈所示)移动到目标位置 (3,2)上。
要将指定块移入目标位置,必须先将空白块移入目标位置,空白块要移动到目标位置,必然是从位置(2,2)上与当前图中目标位置上的棋子交换位置,之后能与空白块交换位置的只有当前图中目标位置上的那个棋子,因此目标棋子永远无法走到它的目标位置, 游戏无法完成。
【数据范围】
对于30%的数据,1≤n,m≤10,q=1;
对于 60%的数据,1≤n,m≤30,q≤10;
对于 100%的数据,1≤n,m≤30,q≤500。
-------------------------------------------------------------------------------------------------------
“45分做法”
dfs
边界为:初始的格子到达了目标格子
空格一直在动,初始的格子在空格子动的时候,可能动也可能不
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,q,xx,yy,ans;
int v[50][50],mrk[45][45][45][45];
int xa[] = {0,1,0,-1};
int ya[] = {1,0,-1,0};
bool jdg(int a,int b)
{
if(a < 1 || a > n )
return false;
if( b < 1 || b > m)
return false;
if(!v[a][b])
return false;
return true;
}
void dfs(int a,int b,int x,int y,int cnt)
{
if(cnt >= ans)
return;
if(x == xx && y == yy)
{
ans = cnt;
return;
}
if(mrk[a][b][x][y] <= cnt)
return;
mrk[a][b][x][y] = cnt;
for(int i = 0; i <= 3; i++)
{
int X = a + xa[i],Y = b + ya[i];
if(jdg(X,Y))
{
if(X == x && Y == y)
dfs(X,Y,a,b,cnt+1);
else
dfs(X,Y,x,y,cnt+1);
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
scanf("%d",&v[i][j]);
for(int i = 1; i <= q; i++)
{
ans = 1e6;
memset(mrk,0x3f,sizeof(mrk));
int a,b,c,d;
scanf("%d%d%d%d%d%d",&a,&b,&c,&d,&xx,&yy);//空格,初始,目标
dfs(a,b,c,d,0);
if(ans == 1e6)
printf("-1\n");
else
printf("%d\n",ans);
}
return 0;
}
正解
(钢哥教的神仙操作
通过数据范围判断复杂度
大概在O(nlogn)到O(n2)之间
还和图有关
spfa!!
可是怎没建图咧
(我一直思考该如何把这些格子连起来
其实,是把不同的可行状态当做点
从一个状态转移到另一个状态所需要移动的最少步数作为边权
用链式前向星来建图
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> #include<cstring> #include<cmath> using namespace std; inline int read() { int sum = 0,p = 1; char ch = getchar(); while(ch < ‘0‘ || ch > ‘9‘) { if(ch == ‘-‘) p = -1; ch = getchar(); } while(ch >= ‘0‘ && ch <= ‘9‘) { (sum *= 10) += ch - ‘0‘; ch = getchar(); } return sum * p; } int n,m,q; int bx,by,sx,sy,ex,ey; int v[35][35];//读入的格子初始状态 int dir[4][2]= {{1,0},{-1,0},{0,1},{0,-1}}; //转移用的矩阵(0是对x,1是对y) int dis[50005],num[35][35][5]; bool flag[35][35]; struct edge { int nxt,to,val; }edge[300005]; int cnt,head[3005];//链式前向星 struct node { int x,y,d; }; void add(int a,int b,int c)//链前加边 { edge[++cnt].nxt = head[a]; edge[cnt].to = b; edge[cnt].val = c; head[a] = cnt; } int bfs(int stx,int sty,int edx,int edy,int rtx,int rty) { if(stx == edx && sty == edy) return 0; memset(flag,0,sizeof(flag)); queue<node>M; node v0; v0.x = stx; v0.y = sty; v0.d = 0; M.push(v0); while(!M.empty()) { node u = M.front(); M.pop(); int x = u.x; int y = u.y; int d = u.d; for(int i = 0;i <= 3;i++) { int xx = x + dir[i][0]; int yy = y + dir[i][1]; if(!v[xx][yy]) continue; if(xx == rtx && yy == rty) continue; if(flag[xx][yy]) continue; if(xx == edx && yy == edy) return d + 1; flag[xx][yy] = 1; node tt; tt.x = xx; tt.y = yy; tt.d = d + 1; M.push(tt); } } return 0x3f3f3f3f; } int spfa() { if(sx == ex && sy == ey) return 0; memset(dis,0x3f,sizeof(dis)); queue<int> M; for(int i = 0;i <= 3;i++) { int x = sx + dir[i][0]; int y = sy + dir[i][1]; if(num[sx][sy][i]) { dis[num[sx][sy][i]] = bfs(bx,by,x,y,sx,sy); M.push(num[sx][sy][i]); } } while(!M.empty()) { int u = M.front(); M.pop(); for(int i = head[u];i != -1;i = edge[i].nxt) { int to = edge[i].to; if(dis[to] > dis[u] + edge[i].val) { dis[to] = dis[u] + edge[i].val; M.push(to); } } } int ans = 0x3f3f3f3f; for(int i = 0;i <= 3;i++) if(num[ex][ey][i]) ans = min(ans,dis[num[ex][ey][i]]); if(ans == 0x3f3f3f3f) return -1; else return ans; } int main() { n = read(),m = read(),q = read(); memset(head,-1,sizeof(head)); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) v[i][j] = read(); int tot = 0; for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) for(int k = 0; k <= 3; k++) { int x = i + dir[k][0]; int y = j + dir[k][1]; if(v[i][j] && v[x][y]) num[i][j][k] = ++tot; } //给每种(可能的状态)编号 for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) for(int k = 0; k <= 3; k++) if(num[i][j][k]) { int x = i + dir[k][0]; int y = j + dir[k][1]; add(num[i][j][k],num[x][y][k^1],1);//^为0-1,2-3 } //加边:交换空点和旁边的点前后两种 for(int i = 1; i <= n;i++) { for(int j = 1; j <= m;j++) { for(int k = 0; k <= 3;k++) { for(int t = 0;t <= 3;t++) { if(k != t && num[i][j][k] && num[i][j][t]); { int x1 = i + dir[k][0]; int y1 = j + dir[k][1]; int x2 = i + dir[t][0]; int y2 = j + dir[t][1]; add(num[i][j][k],num[i][j][t],bfs(x1,y1,x2,y2,i,j)); } } } } } //加边 while(q--) { bx = read(),by = read(); sx = read(),sy = read(); ex = read(),ey = read(); printf("%d\n",spfa()); } return 0; }
--------------------------------------------------------------------------------
for循环多了不加括号有惊喜(惊吓)??!
xx x ex什么的变量名最头痛了
忘初始化了解一下
以上是关于华容道的主要内容,如果未能解决你的问题,请参考以下文章