zoukankan      html  css  js  c++  java
  • [luogu1979] 华容道

    题面

    ​ 先讲点无关的,这道题是真的恶心...

    ​ 好了,第一眼看到这道题,肯定是准备暴搜的,但是想了一想,省选难度的题目不可能一上来就让你暴搜吧,于是开启了无穷无尽的分析,我们不妨设指定棋子叫做移动棋,所以题目的目标就是利用一个空格子,将移动棋移动至目标点,然后就开始想怎么移动呢???

    ​ 如果移动棋上下左右都是棋子的话,肯定不能移动,所以必定要一个空格子来把棋子移出去,所以棋子移动的条件便是周围有空格子,此时可以把移动棋移动至空格子处,空格子就到了移动棋原来的位置上,这样往返,不断地将空格子移至移动棋旁边,这样就可以将移动棋一步步地向目标点移去,所以移动的方面分析完了,接下来分析如何把当前的状态表示出来呢???

    ​ 我们可以设一个三元对(x, y, k)代表点(x, y)的 k 方向为空格子,不妨设空格子的坐标为(a, b, k)此时我们有两种操作:①:将 k 的值变更,即当前点不动,将空格子移至当前点的其他的方位;②:交换当前点与空格子,此时当前点的坐标变为原来空格子的坐标,那么空格子所对的方向是什么呢???我们不妨设上右下左为0,1,2,3,则原来点的坐标变为(a, b, (k + 2) % 4)(空格子由上变下,下变上,左变右,右变左)。所以我们可以先预处理一下每个可以移动的棋子的这几种情况。

    预处理代码

    inline void BFS(int t)
    {
    	memset(d, -1, sizeof(d)); mapp[xa][ya] = 0; 
    	queue <node> q;
    	q.push((node) { xb, yb }); d[xb][yb] = 0;
    	while(!q.empty())
    	{
    		node tmp = q.front(); q.pop(); 
    		for(int k = 0; k < 4; k++)
    		{
    			int tx = tmp.x + xx[k], ty = tmp.y + yy[k];
    			if(d[tx][ty] != -1 || !mapp[tx][ty]) continue;
    			d[tx][ty] = d[tmp.x][tmp.y] + 1; q.push((node) { tx, ty }); 
    		}
    	}
    	mapp[xa][ya] = 1; 
    	if(t == 4) return; //每次询问时,从空格向起点跑计算出d数组后就不需要再加边了.
    	for(int k = 0; k < 4; k++)
    	{
    		int tx = xa + xx[k], ty = ya + yy[k];
    		if(d[tx][ty] != -1) add(xa * 120 + ya * 4 + t, xa * 120 + ya * 4 + k, d[tx][ty]); 
            //状态为点,状态的转移为边,长度为所需要的最少步数.
    	}
    	add(xa * 120 + ya * 4 + t, xb * 120 + yb * 4 + (t + 2) % 4, 1); //交换所需要的最少步数
    }
    
    

    ​ 在这之后,每对于一次询问,我们就可以先将空格子移动至移动棋的四个方向的最少步数算出来,在这里,我们要使用一种思想,就是说,将每个三元对(x, y, k)化为一个一维的状态,x * 120 + y * 4 + k(我们其实可以把这个三元对看做一个百位为x,十位为y,个位为k的一个数,其中个位逢4进1,因为k只有四种可能,十位逢30进1,因为y<=30,百位逢120进1,因为每一个y有4种可能,又有30个y,所以x便逢120进1,这样保证了不同种状态不会表示同一个数),最后以各个状态为点,状态的转移为边(也就是这两种情况的转移所需要的最少步数),跑一遍SPFA,最后统计空格在终点的四个方向的最小值就可以了。

    完整代码

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    using namespace std;
    
    int n, m, Q, head[5005], cnt, mapp[37][37], xa, xb, ya, yb, ex, ey, d[37][37], dis[5005], vis[5005], xx[4] = { 0, 1, 0, -1 }, yy[4] = { 1, 0, -1, 0 }; 
    struct node
    {
    	int x, y; 
    };
    struct Edge
    {
    	int to, next, cost; 
    } edge[20005]; 
    
    inline int read()
    {
    	int x = 0, w = 1;
    	char c = getchar();
    	while(c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); }
    	while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    	return x * w;
    }
    
    inline void add(int u, int v, int w) { edge[++cnt].to = v; edge[cnt].cost = w; edge[cnt].next = head[u]; head[u] = cnt; }
    
    inline void BFS(int t)sousuo
    {
    	memset(d, -1, sizeof(d)); mapp[xa][ya] = 0;
    	queue <node> q;
    	q.push((node) { xb, yb }); d[xb][yb] = 0;
    	while(!q.empty())
    	{
    		node tmp = q.front(); q.pop(); 
    		for(int k = 0; k < 4; k++)
    		{
    			int tx = tmp.x + xx[k], ty = tmp.y + yy[k];
    			if(d[tx][ty] != -1 || !mapp[tx][ty]) continue;
    			d[tx][ty] = d[tmp.x][tmp.y] + 1; q.push((node) { tx, ty }); 
    		}
    	}
    	mapp[xa][ya] = 1; 
    	if(t == 4) return;
    	for(int k = 0; k < 4; k++)
    	{
    		int tx = xa + xx[k], ty = ya + yy[k];
    		if(d[tx][ty] != -1) add(xa * 120 + ya * 4 + t, xa * 120 + ya * 4 + k, d[tx][ty]); 
    	}
    	add(xa * 120 + ya * 4 + t, xb * 120 + yb * 4 + (t + 2) % 4, 1); 
    }
    
    inline void SPFA()
    {
    	queue<int> q; 
    	memset(dis, 127, sizeof(dis)); memset(vis, 0, sizeof(vis));
    	for(int i = 0; i < 4; i++)
    	{
    		int tx = xa + xx[i], ty = ya + yy[i]; int id = xa * 120 + ya * 4 + i; 
    		if(d[tx][ty] != -1) { q.push(id); vis[id] = 1; dis[id] = d[tx][ty]; }
            //空格有可能到起点的四种方向,需要都统计进去.
    	}
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		for(int i = head[u]; i; i = edge[i].next)
    		{
    			int v = edge[i].to;
    			if(dis[v] > dis[u] + edge[i].cost)
    			{
    				dis[v] = dis[u] + edge[i].cost;
    				if(!vis[v]) { vis[v] = 1; q.push(v); }
    			}
    		}
    		vis[u] = 0; 
    	}
    }
    
    int main()
    {
    	n = read(); m = read(); Q = read();
    	for(int i = 1; i <= n; i++)
    		for(int j = 1; j <= m; j++)
    			mapp[i][j] = read(); 
    	for(int i = 1; i <= n; i++)
    		for(int j = 1; j <= m; j++)
    		{
    			if(!mapp[i][j]) continue;
    			xa = i; ya = j;
    			for(int k = 0; k < 4; k++)
    			{
    				xb = xa + xx[k]; yb = ya + yy[k];
    				if(mapp[xb][yb]) BFS(k); 
    			}
    		}
    	while(Q--)
    	{
    		xb = read(); yb = read(); xa = read(); ya = read(); ex = read(); ey = read();
    		if(xa == ex && ya == ey) { puts("0"); continue; }
    		BFS(4); SPFA(); int ans = 2e9 + 7;
    		for(int i = 0; i < 4; i++) ans = min(ans, dis[ex * 120 + ey * 4 + i]);
    		printf("%d
    ", ans == 2e9 + 7 ? -1 : ans); 
    	}
    	return 0;
    }
    
    

  • 相关阅读:
    软件设计和开发是手艺活也是艺术活
    学界老师和业界专业人员的紧密合作才能促进软件设计开发教学的进步
    最简单的 GitExtensions 教程(持续更新中)
    最简单的 IntelliJ IDEA 中使用 GitHub 进行版本控制教程(持续更新中)
    工作室成员 GitHub 地址集中贴(按发布时间先后排序)
    使用 Visual Studio Code 运行 C# 及 Java 程序
    推荐一个非常好的 IntelliJ IDEA 教程
    Commit message 和 Change log 编写指南(转自阮一峰的博客)
    关于编码规范的延伸资料(来自于福州大学陈世发同学的博客)
    【扩展阅读】提问的智慧(转自福州大学陈世发同学的评论)
  • 原文地址:https://www.cnblogs.com/ztlztl/p/10428254.html
Copyright © 2011-2022 走看看