题目大意:
输入w h,接下来输入h行w列的图
' . ':干净的点; ' * ' :垃圾; ' x ' : 墙; ' o ' : 初始位置;
输出 清理掉所有垃圾的最短路径长度 无则输出-1
Sample Input
7 5
.......
.o...*.
.......
.*...*.
.......
15 13
.......x.......
...o...x....*..
.......x.......
.......x.......
.......x.......
...............
xxxxx.....xxxxx
...............
.......x.......
.......x.......
.......x.......
..*....x....*..
.......x.......
10 10
..........
..o.......
..........
..........
..........
.....xxxxx
.....x....
.....x.*..
.....x....
.....x....
0 0
Sample Output
8
49
-1
题解
用bfs求出任意两点间的距离然后转换成tsp问题,用dfs解
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* 题意:给一个 n*m的图,有一个机器人从一点开始清理垃圾,要求把所有的垃圾清理完,求最短的路径 思路:这个题可以先用bfs求出任意两点间的距离然后转换成tsp问题,用dfs解。 这个题还可以用状态压缩bfs写,但写了一次没过,再试试。 */ #include <cstdlib> #include <stdio.h> #include <string.h> #include <queue> #define INF 0x7fffffff #define N 22 using namespace std; struct node { int x,y,step; }p[N*N],s,e; char map[N][N]; int n,m,d[4][2]={0,1,1,0,0,-1,-1,0},used[N][N]; int dis[N][N],ans,use[N],cnt; bool ok(int x,int y) { return x>=0&&x<n&&y>=0&&y<m&&map[x][y]!='x'&&!used[x][y]; } int bfs() { memset(used,0,sizeof(used)); int i; queue<node> q; q.push(s); while(!q.empty()) { node cur=q.front(),next; q.pop(); for(i=0;i<4;i++) { next.x=cur.x+d[i][0]; next.y=cur.y+d[i][1]; next.step=cur.step+1; if(ok(next.x,next.y)) { used[next.x][next.y]=1; if(next.x==e.x&&next.y==e.y) return next.step; q.push(next); } } } return INF; } void dfs(int s,int num,int length) { int j; if(num==0&&ans>length) { ans=length; return ; } if(length>ans) return ; for(j=0;j<cnt;j++) { if(dis[s][j]!=INF&&!use[j]) { use[j]=1; length+=dis[s][j]; dfs(j,num-1,length); num+1; length-=dis[s][j]; use[j]=0; } } } int main() { int i, j, start, flag; while(scanf("%d%d",&m,&n)&&(n||m)) { cnt=flag=0; for(i=0;i<n;i++) { scanf("%s",map[i]); for(j=0;j<m;j++) { if(map[i][j]=='o'||map[i][j]=='*') { p[cnt].x=i,p[cnt].y=j; if(map[i][j]=='o') start=cnt; cnt++; } dis[i][j]=INF; } } //memset(dis,INF,sizeof(dis)); for(i=0;i<cnt;i++) for(j=i+1;j<cnt;j++) { s=p[i],e=p[j],s.step=0; dis[i][j]=dis[j][i]=bfs(); if(dis[i][j]==INF) { flag=1; break; } } /* for(i=0;i<cnt;i++) { for(j=0;j<cnt;j++) printf("%d ",dis[i][j]); puts(""); }*/ if(flag) puts("-1"); else { ans=INF; memset(use,0,sizeof(use)); use[start]=1; dfs(start,cnt-1,0); printf("%d ",ans); } } return 0; }
状压 BFS
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; struct NODE { int ni,nj,mask,len; NODE(){}; NODE(int n_i,int n_j,int nmask,int nlen): ni(n_i),nj(n_j),mask(nmask),len(nlen){}; }; char G[35][35]; int w,h,cnt,st_i,st_j; int vis[35][35][1<<11],flag[35][35]; int mi[]={0,0,1,-1},mj[]={1,-1,0,0}; bool bound(int i,int j) /// 判断是否越界 { return (i>h||i<1||j>w||j<1); } bool OK(int mask) /// 判断垃圾是否被清理 { for(int i=0;i<cnt;i++) if(mask & (1<<i)); /// 用mask二进制的各个位表示各个垃圾 else return 0; return 1; } int BFS() { memset(vis,0,sizeof(vis)); int ans=INF; queue<NODE> q; q.push(NODE(st_i,st_j,0,0)); /// 构造函数的便利之处 while(!q.empty()) { NODE tmp=q.front(); q.pop(); if(OK(tmp.mask)) /// 返回1表明垃圾清理光了 就更新ans值 { ans=min(ans,tmp.len); continue; } if(vis[tmp.ni][tmp.nj][tmp.mask]) continue; /// 该点的mask状态已出现过 则忽略该点 else vis[tmp.ni][tmp.nj][tmp.mask]=1; /// 未出现过 则标记为已出现过 for(int i=0;i<4;i++) { int newi=tmp.ni+mi[i],newj=tmp.nj+mj[i],newmask=tmp.mask; if(bound(newi,newj)||G[newi][newj]=='x') continue; /// 若越界或该点为墙 则忽略该点 if(G[newi][newj]=='*') newmask=tmp.mask | (1<<flag[newi][newj]); /* 假如目前是三号垃圾 更新状态 即若此时tmp.mask的二进制为011(第一二号垃圾已清除) newmask=011 | 1<<3=111(第一二三号垃圾已清除) 很明显 将垃圾状态压缩 用mask的二进制表示 */ if(vis[newi][newj][newmask]) continue; /// 若该新点的该状态已出现过 则忽略该点 若无则放入队列 q.push(NODE(newi,newj,newmask,tmp.len+1)); } } return ans==INF ? -1:ans; } int main() { while(~scanf("%d%d",&w,&h)&&(w||h)) { cnt=0; for(int i=1;i<=h;i++) for(int j=1;j<=w;j++) { scanf(" %c",&G[i][j]); if(G[i][j]=='o') st_i=i,st_j=j; /// 记下所在位置 else if(G[i][j]=='*') flag[i][j]=cnt++; /// 记下垃圾的位置 并为其编号 防止重复清扫 } if(!cnt) printf("0 "); else printf("%d ",BFS()); } return 0; }