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
Output
Input
Output
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;
}
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;
}
相关文章推荐
- android中dip、px相互换算
- struts2: tag s:property must be empty, but is not
- 练手程序120个(转载)
- 如果获取设备的的分辨率、屏幕尺寸、像素密度?
- Ofbiz模块加载机制即创建独立模块(脱离热部署)
- react native白屏及性能优化
- 相册管理系统
- 关于大小端字节序
- jquery ajax的参数以及使用方法详解
- 处理事件的方式:两种类的覆盖处理(自己管理,覆盖专用事件函数;自己统一管理,覆盖QWidget::Event通用函数),一种对象的处理(父控件统一管理,即安装过滤器),两种全局处理(QCoreApplication安装过滤器;覆盖notify方法)
- [React Native] Passing data when changing routes
- 我接下来要走的基础路
- PHP的51个Memcached方法(1-11)
- 368. Largest Divisible Subset
- Gson:将Java对象转换为JsonObject
- android的一些知识小记
- CyclicBarrier的使用
- (HDU 1166) 敌兵布阵
- 利用Fiddler手机抓包对ONE·APP网页爬虫实现电影资讯微信Java开发
- QObject的event函数就可以改写对消息的处理