您的位置:首页 > 其它

CodeForces 197D Infinite Maze (BFS+状态压缩)

2016-07-25 02:31 267 查看
题目链接:http://codeforces.com/problemset/problem/197/D

D. Infinite Maze

time limit per test
2 seconds

memory limit per test
256 megabytes

input
standard input

output
standard output

We've got a rectangular n × m-cell maze. Each cell is either passable, or is a wall (impassable). A little boy found the maze and cyclically tiled a plane with it so that the plane became an infinite maze.
Now on this plane cell (x, y) is a wall if and only if cell


is a wall.

In this problem

is a remainder of dividing number
a by number b.

The little boy stood at some cell on the plane and he wondered whether he can walk infinitely far away from his starting position. From cell
(x, y) he can go to one of the following cells:
(x, y - 1),
(x, y + 1), (x - 1, y) and
(x + 1, y), provided that the cell he goes to is not a wall.

Input
The first line contains two space-separated integers n and
m (1 ≤ n, m ≤ 1500) — the height and the width of the maze that the boy used to cyclically tile the plane.

Each of the next n lines contains
m characters — the description of the labyrinth. Each character is either a "#", that marks a wall, a ".", that marks a passable cell, or an "S",
that marks the little boy's starting point.

The starting point is a passable cell. It is guaranteed that character "S" occurs exactly once in the input.

Output
Print "Yes" (without the quotes), if the little boy can walk infinitely far from the starting point. Otherwise, print "No" (without the quotes).

Examples

Input
5 4
##.#
##S#
#..#
#.##
#..#


Output
Yes


Input
5 4
##.#
##S#
#..#
..#.
#.##


Output
No


Note
In the first sample the little boy can go up for infinitely long as there is a "clear path" that goes vertically. He just needs to repeat the following steps infinitely: up, up, left, up, up, right, up.

In the second sample the vertical path is blocked. The path to the left doesn't work, too — the next "copy" of the maze traps the boy.

1 题目分析
将题目分析得到,我们需要解决的是,遍历整个图的所有节点,然后求出最小其权值的最小值。
2 解题思路
 由于整张图中的所有点默认标号为1-16,所以我们可以采用深度优先遍历的方式来遍历整张图,然后记录下用时最短的那个时间即可。
3 存在的问题
 题目中所给最大的编号是16,遍历整张图需要进行16!次操作,时间太长,绝对超时。
4 解决方案
  利用深度优先的方式,在其中进行剪枝操作。
  剪枝分为两种:1 可行性剪枝 2 最优性剪枝。在这里我们只用到后者。
剪枝1 如果在进行深度优先时已经花费的时间大于了已有的最优时间,那么不进行接下来的操作。
剪枝2 记录从出发点1-某个点i的状态,当两个状态相同时,如果前者花费的时间少,那么后者不需要再进行操作。
比如 1-4
状态1 :1 2 3 4  花费时间 :5
状态2 :1 3 2 4  花费时间 :8
由于比较时,前者是已经走完的,那么当后者花费时间比较大的时候,我们直接采取前者的路线即可,不需要再进行自己的操作。
5 难点
如何进行状态的存储?
利用二维数组State[i][j];i表示现在走到的岛屿编号,j代表已经走过的路线。
也许你会问j怎么代表路线呢?
我们利用二进制来存储已经走过的路线,走过就将对应位置设为1,比如:
f[2][5]表示现在在第二个岛屿,之前走过了两个岛屿,因为5二进制表示为101;

BFS+状态压缩AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=20;
int f[maxn][1<<maxn],map[maxn][maxn];
int n,m,ans,vis[maxn];
void dfs(int now,int condition)
{
if(f[now][condition]>ans)
return;
if((condition|(1<<(n-1)))==(1<<n)-1){
if(f[now][condition]+map[now]
<ans)
{
ans=f[now][condition]+map[now]
;
}
}
for(int i=2;i<=n-1;i++)
if(vis[i]==0)
{
int state=condition|(1<<(i-1));
if(now==1)
{
vis[i]=1;
f[i][state]=map[1][i];
dfs(i,state);
vis[i]=0;
}
else if(f[i][state]>f[now][condition]+map[now][i])
{
vis[i]=1;
f[i][state]=f[now][condition]+map[now][i];
dfs(i,state);
vis[i]=0;
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&map[i][j]);
memset(f,127/3,sizeof(f));
//cout << f[1][1] << endl;
ans=1000000000;
f[1][1]=0;
dfs(1,1);
cout << ans << endl;
}


纯状态压缩AC代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<limits>
using namespace std;
const int maxn=20;
int dp[1<<maxn][maxn],map[maxn][maxn];
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i=0;i<n;i++)
for(int j=0;j<n;j++) scanf("%d",&map[i][j]);
for(int i=0;i<n-2;i++)
dp[(1<<i)][i]=map[0][i+1];
int ans=n-2;
int flag=0;
for(int k=0;k<(1<<ans);k++)
{
for(int i=0;i<n;i++)
if(k==(1<<i))
flag=1;
if(flag==1){
flag=0;
continue;
}
for(int i=0;i<ans;i++) dp[k][i]=100000000;
for(int i=0;i<ans;i++)
{
if(k&(1<<i)){
for(int j=0;j<n;j++){
if(i==j) continue;
if(k&(1<<j)) dp[k][i]=min(dp[k][i],dp[k-(1<<i)][j]+map[j+1][i+1]);
}
}
}
}
int sum=numeric_limits<int>::max();
int t=(1<<ans)-1;
for(int i=0;i<ans;i++)
sum=min(sum,dp[t][i]+map[i+1][ans+1]);
printf("%d\n",sum);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: