CF225D Snake 解题报告 *

CF225D Snake 解题报告

1 题目链接

https://codeforces.com/problemset/problem/225/D

2 题目整理

题目 : 蛇

题目描述

让我们提醒你一个非常流行的游戏的规则,称为“蛇”(有时是“蟒蛇”,“蟒蛇”或“蠕虫”)。

场地用一个 n × m n \times m n×m的矩形表表示场地的一些方格被认为是不可通过的,场地的所有其他方格都是可以通过的。

你控制一条蛇,蛇是由 k k k个部分组成的。每个段占用一个可通过的正方形,但任何可通过的正方形最多包含一个段。所有的段都是由 1 1 1 k k k的整数索引的, k k k是蛇的长度。第 1 1 1段是头,第 k k k段是尾。对于任意 i i i,索引为 i i i i + 1 i + 1 i+1的段位于字段的相邻正方形中,也就是说,这些正方形共享一条公共边。

其中一个可通过的方框包含一个苹果。蛇的目标是到达苹果并吃掉它(也就是说,把它的头和苹果放在一个正方形的位置上)。

蛇在整个游戏中都在移动。在一次移动中,蛇可以将它的头移动到相邻的场地方格。所有其他部分都跟随头部。也就是说,每个线段 i i i移动到刚刚有线段 i − 1 i - 1 i1的方块上。考虑所有部分包括头部同时移动(见第二个测试样本)。如果蛇的头移动到一个不能通过的方块,或者移动到被它的其他部分占据的方块,蛇就会死亡。这就是为什么我们认为这些举措是无效的。

你的任务是确定蛇到达苹果所需的最小有效移动数。

输入格式

第一行包含两个用空格分隔的整数 n n n m m m——游戏场场地的行数和列数。

接下来的 n n n行描述了游戏领域。每一行包含 m m m个字符。字符“#”代表一堵墙,“。”是一个可通行的正方形,“@”是一个苹果。蛇的第一个部分由字符 “ 1 ” “1” 1表示,第二个部分由字符 “ 2 ” “2” 2表示,以此类推。

游戏字段描述不能包含除“#”、“。”、“@”以外的任何字符和数字(除 0 0 0外),以保证描述字段的正确性。它保证描述的字段包含一个苹果和一条蛇,蛇的长度至少是 3 3 3,最多是 9 9 9

输出格式

输出一个整数——达到苹果所需的最小移动数。如果蛇够不到苹果,打印 − 1 -1 1

样例输入1
4 5
##...
..1#@
432#.
...#.
样例输出1
4
样例输入2
4 4
#78#
.612
.543
..@.
样例输出2
6
样例输入3
3 2
3@
2#
1#
样例输出3
-1
数据范围

对于 100 % 100\% 100%的数据:

  • 1 ≤ n , m ≤ 15 1 \leq n, m \leq 15 1n,m15
  • 3 ≤ k ≤ 9 3 \leq k \leq 9 3k9

3 题意分析

3.1 题目大意

在一个棋盘上有一条蛇,问蛇吃到苹果的最短距离。

3.2 样例分析

如上所述。

4 解法分析

这道题是一道变态超难无敌让我炸裂的广搜题。

我写代码 0.5 0.5 0.5个小时,调试 1.5 1.5 1.5个小时,想题想了整整 2 2 2天…

首先,看题目,我们可以知道这是一条蛇,而深搜又会超时,所以我们只能用广搜。蛇的广搜其实和普通的广大体相同,唯一一个不同点就是关于判重的方法。因为只有两条蛇身体坐标完全相同才可以不走,所以这里我们要用状态压缩。

状态压缩我想出了两种方法:第一种是给每个格子编号,把蛇的头和剩余部分分开,剩余部分就用格子的编号来压成一个数。(注意这里要用长整数)。第二种是先记录头,然后通过记录每个部分上一节走向这一节的方向。第二种空间相对少一点,时间也少一些,但是比较复杂。

这样,就把蛇走迷宫转化成了一个普通的广搜啦(完结撒花✿✿ヽ(°▽°)ノ✿)

AC代码

ACCode #001
// From xlk
// Rating 2428
// reason : 思路清晰,代码简洁明了简短,运用了set
#include<stdio.h>
#include<set>
using namespace std;
int n,m,l;
char s[20][20];
set<long long>v;
struct N
{
	int x[10],y[10],d;
}q[131077];
int b,f,dx[]={1,-1,0,0},dy[]={0,0,1,-1};
long long H(const N&_)
{
	long long w=0;
	for(int i=0;i<l;i++)
		w+=13131*w+131*_.x[i]+_.y[i];
	return w;
}
int main()
{
	scanf("%d %d",&n,&m);
	for(int i=0;i<n;i++)
		scanf("%s",s[i]);
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			if(s[i][j]>='0'&&s[i][j]<='9')
			{
				q[0].x[s[i][j]-'0'-1]=i,q[0].y[s[i][j]-'0'-1]=j;
				l=max(l,s[i][j]-'0');
			}
	v.insert(H(q[0]));
	for(b=0,f=1;b!=f;++b&=131071)
	{
		for(int i=0,j;i<4;i++)
		{
			int x=q[b].x[0]+dx[i],y=q[b].y[0]+dy[i];
			if(x<0||x>=n||y<0||y>=m)
				continue;
			for(j=0;j<l-1;j++)
				if(x==q[b].x[j]&&y==q[b].y[j])
					break;
			if(j<l-1||s[x][y]=='#')
				continue;
			if(s[x][y]=='@')
			{
				printf("%d\n",q[b].d+1);
				return 0;
			}
			q[f].x[0]=x,q[f].y[0]=y;
			for(j=1;j<l;j++)
				q[f].x[j]=q[b].x[j-1],q[f].y[j]=q[b].y[j-1];
			q[f].d=q[b].d+1;
			long long w=H(q[f]);
			if(v.count(w))
				continue;
			v.insert(w),++f&=131071;
		}
	}
	puts("-1");
}
ACCode #002
// From He_Ren
// Rating 3020
// reason : 思路清晰代码简洁明了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int MAXN = 15 + 5;
const int MAXM = 15 + 5;
const int ALL = (1<<16) + 5;
const int dx[] = {0,1,0,-1};
const int dy[] = {1,0,-1,0};
 
int d, all;
char s[MAXN][MAXM];
 
typedef int State;
inline State make_State(int x,int y,int k){ return x << 20 | y << 16 | k;}
inline void get_State(int &x,int &y,int &k,const State &p){ x = p >> 20; y = (p >> 16) & 15; k = p & all;}
 
int dfs_beg(int x,int y,int cur)
{
	if(cur == 9){ d = 9; return 0;}
	for(int k=0; k<4; ++k)
	{
		int xx = x + dx[k], yy = y + dy[k];
		if(s[xx][yy] - '0' == cur+1)
			return dfs_beg(xx,yy,cur+1) << 2 | k;
	}
	d = cur; return 0;
}
 
int can[ALL];
bool chk(int mask)
{
	if(can[mask] != -1) return can[mask];
	int &res = can[mask] = 1;
	
	static bool vis[30][30];
	stack<pii> sta;
	
	int x = 9, y = 9;
	vis[x][y] = 1; sta.push(make_pair(x,y));
	for(int i=1; i<d; ++i)
	{
		int k = mask & 3; mask >>= 2;
		x += dx[k]; y += dy[k];
		if(vis[x][y]){ res = 0; break;}
		vis[x][y] = 1; sta.push(make_pair(x,y));
	}
	while(sta.size()) vis[sta.top().first][sta.top().second] = 0, sta.pop();
	return res;
}
 
int main(void)
{
	memset(can,-1,sizeof(can));
	
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1; i<=n; ++i) scanf("%s",s[i]+1);
	
	State beg;
	for(int i=1; i<=n; ++i)
		for(int j=1; j<=m; ++j)
			if(s[i][j] == '1')
			{
				beg = make_State(i, j, dfs_beg(i,j,1));
				break;
			}
	all = (1 << ((d-1) * 2)) - 1;
	
	queue<State> q;
	map<State,int> dis;
	dis[beg] = 0; q.push(beg);
	while(q.size())
	{
		int x,y,mask;
		int cur = dis[q.front()];
		get_State(x,y,mask,q.front()); q.pop();
		
		for(int k=0; k<4; ++k)
		{
			int xx = x + dx[k], yy = y + dy[k];
			if(!s[xx][yy] || s[xx][yy] == '#') continue;
			if(s[xx][yy] == '@') return printf("%d",cur+1), 0;
			
			State nxt = make_State(xx, yy, (mask << 2 | (k^2)) & all);
			if(!chk(nxt & all) || dis.find(nxt) != dis.end()) continue;
			dis[nxt] = cur + 1;
			q.push(nxt);
		}
	}
	printf("-1");
	return 0;
}

ACCode #003
// From rng_58
// Rating 3084
// reason : 思路清晰代码简洁明了
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <utility>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstdio>

using namespace std;

#define REP(i,n) for((i)=0;(i)<(int)(n);(i)++)
#define foreach(c,itr) for(__typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++)

int X,Y,D;
string board[20];

int dx[] = {1,-1,0,0}, dy[] = {0,0,1,-1};
queue <vector <int> > q;
map <vector <int>, int> mp;

bool used[20][20];

void dfs(int x, int y){
    int i;
    
    if(x < 0 || x >= X || y < 0 || y >= Y || board[x][y] == '#' || used[x][y]) return;
    used[x][y] = true;
    
    REP(i,4) dfs(x+dx[i], y+dy[i]);
}

int main(void){
    int i,j,k;
    
    cin >> X >> Y;
    REP(i,X) cin >> board[i];
    
    vector <int> init;
    for(k=0;;k++){
        bool found = false;
        REP(i,X) REP(j,Y) if(board[i][j] == '1' + k){
            found = true;
            init.push_back(i);
            init.push_back(j);
        }
        if(!found) break;
    }
    D = k;
    
    dfs(init[0], init[1]);
    bool can = false;
    REP(i,X) REP(j,Y) if(board[i][j] == '@' && used[i][j]) can = true;
    if(!can){
        cout << -1 << endl;
        return 0;
    }
    
    q.push(init);
    mp[init] = 0;
    
    while(!q.empty()){
        vector <int> v = q.front();
        q.pop();
        int d = mp[v];
        
        REP(i,4){
            int x = v[0] + dx[i], y = v[1] + dy[i];
            if(x < 0 || x >= X || y < 0 || y >= Y || board[x][y] == '#') continue;
            bool bad = false;
            REP(j,D-1) if(x == v[2*j] && y == v[2*j+1]) bad = true;
            if(bad) continue;
            
            if(board[x][y] == '@'){
                cout << d + 1 << endl;
                return 0;
            }
            
            vector <int> w(2*D);
            w[0] = x; w[1] = y;
            REP(j,2*D-2) w[2+j] = v[j];
            if(mp.find(w) == mp.end()){
                mp[w] = d + 1;
                q.push(w);
            }
        }
    }
    
    cout << -1 << endl;
    
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值