题意:给你一张\(r*c\)的地图,有’S’,’X’,’.’三种地形,所有判定相邻与行走都是四连通的.我们设’X’为陆地,一个’X’连通块为一个岛屿,’S’为浅水,’.’为深水。刚开始你可以降落在任一一块陆地上,在陆地上可以行走,在浅水里可以游泳。并且陆地和浅水之间可以相互通行.但无论如何都不能走到深水。你现在要求通过行走和游泳使得你把所有的岛屿都经过一边.Q:你最少要经过几个浅水区?保证有解.\(r,c<=50\),联通块\(<=15.\)
分析:这道题的思路很好想,就是毒瘤.集合了\(dfs\)求联通块,\(bfs\)求最短路,状压\(dp\)多种算法.我在每个算法上都挂了半个小时......(谁能想到我现在用\(dfs\),\(bfs\)都各种问题呢\(???\))
联通块个数\(<=15\)???状压\(!!!\)设\(f[i][j]\)表示当前走到了第i个联通块,走过的联通块的集合为\(j\)时经过的最少的浅水区.
\(f[i][j]=min(f[i][j],f[k][j\)^\((1<<(i-1))])+dis[i][k]\),其中\(dis[i][k]\)表示第i个联通块和第\(k\)个联通块之间的最短距离.
所以我们只要\(dfs\)预处理出所有的联通块,然后求任意两个联通块之间的最短路,然后就可以\(DP\)了.
这题最简单的一个部分竟然是\(DP???\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
int x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*o;
}
const int N=55;
int n,m,num,ans=1e9,f[20][100005];
int a[N][N],bel[N][N],visit[N][N],dis[20][20];
int dx[4]={0,0,1,-1},
dy[4]={1,-1,0,0};
vector<pair<int,int> >g[20];
inline void dfs(int x,int y){
bel[x][y]=num;g[num].push_back(pair<int,int>(x,y));
for(int i=0;i<4;++i){
int xx=x+dx[i],yy=y+dy[i];
if(a[xx][yy]!=2||bel[xx][yy])continue;
dfs(xx,yy);
}
}
int main(){
n=read();m=read();
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
char ch;cin>>ch;
if(ch=='X')a[i][j]=2;
if(ch=='S')a[i][j]=1;
}
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
if(a[i][j]==2&&!bel[i][j])++num,dfs(i,j);
memset(dis,0x3f,sizeof(dis));
for(int i=1;i<=num;++i)dis[i][i]=0;
for(int i=1;i<=num;++i){
memset(visit,0x3f,sizeof(visit));
queue<pair<int,int> >q;
for(int j=0;j<(int)g[i].size();++j)q.push(g[i][j]),visit[g[i][j].first][g[i][j].second]=0;
while(q.size()){
int x=q.front().first,y=q.front().second;q.pop();
for(int j=0;j<4;++j){
int xx=x+dx[j],yy=y+dy[j];
if(!a[xx][yy]||bel[xx][yy]==i)continue;
if(a[xx][yy]==2){
dis[i][bel[xx][yy]]=min(dis[i][bel[xx][yy]],visit[x][y]);
if(visit[xx][yy]>visit[x][y]){
visit[xx][yy]=visit[x][y];
q.push(pair<int,int>(xx,yy));
}
}
if(a[xx][yy]==1){
if(visit[xx][yy]>visit[x][y]+1){
visit[xx][yy]=visit[x][y]+1;
q.push(pair<int,int>(xx,yy));
}
}
}
}
}
memset(f,0x3f,sizeof(f));
for(int i=1;i<=num;++i)f[i][1<<(i-1)]=0;
for(int j=1;j<(1<<num);++j){
for(int i=1;i<=num;++i){
if(!(j&(1<<(i-1))))continue;
for(int k=1;k<=num;++k){
if(!(j&(1<<(k-1))))continue;
if(i==k)continue;
f[i][j]=min(f[i][j],f[k][j^(1<<(i-1))]+dis[k][i]);
}
}
}
for(int i=1;i<=num;++i)ans=min(ans,f[i][(1<<num)-1]);
printf("%d\n",ans);
return 0;
}