题面有点坑,翻译内容中没有指明n的范围,通过观察原题面得到$n \leq 15$并大致猜测这是一个状态压缩dp
最小生成树显然不可行,可以举例说明存在某种情况某边要经过两次或更多
对于任意一个岛屿$i$到任意一个岛屿$j$的最短距离显然是固定的,每个岛域之间的距离(不经过其他岛屿)可以用bfs预处理出来,初始状态只要将这个岛屿全部坐标位置全部入队即可
得到了任意两个岛屿之间的直接距离后(当然存在部分岛屿不可互达),在$ n \leq 15$时可以直接用$floyd$求解
现在得到任意两个节点之间的最短距离,显然直接dfs复杂度任然爆炸,dfs求解的复杂度是$O(n!)$
考虑状态压缩dp,设$S$为当前节点是否经过的状态集合,$v$为当前停留的节点,显然有$f(S,v)=min\{ f(S‘,u)+dis(u,v)\}$其中$S‘$中$v$为未达,很明显对于任意一个状态他最多转移$n$个状态
得到时间复杂度为$O(2^nn)$的状压
```
//2019/7/24->Riko->AtNCU->luoguP3070
#include<bits/stdc++.h>
template <typename T> inline void smin (T& x, T y) { if (x > y) x = y;}
//Believing heart is your power
using namespace std;
int getch () {
int ch = getchar();
return (ch == ‘.‘ or ch == ‘S‘ or ch == ‘X‘) ? ch : getch();
}
const int N = 55;
struct Point {
int x, y;
Point (int x, int y) : x(x), y(y) {}
};
struct Len {
int x, y, l;
Len (int x, int y, int l) : x(x), y(y), l(l) {}
};
int r, c, n, ind;
int type[N][N], idx[N][N], len[N][N], vis[N][N], hasgone[N];
int f[(1<<15)+64][16], Mx[4] = {0, 0, 1, -1}, My[4] = {1, -1, 0, 0};
queue <Point> Find, Save[N];
queue <Len> Que;
void Getpoints (int x, int y) {
Find.push(Point(x, y)); Save[ind].push(Point(x, y));
while (!Find.empty()) {
Point tem = Find.front(); Find.pop();
for (int i = 0; i < 4; ++i) {
int Nx = tem.x+Mx[i];
int Ny = tem.y+My[i];
if (idx[Nx][Ny]) continue;
if (type[Nx][Ny] != 1) continue;
idx[Nx][Ny] = ind;
Find.push(Point(Nx, Ny));
Save[ind].push(Point(Nx, Ny));
}
}
}
void Getlen (int id) {
while (!Save[id].empty()) {
Point tem = Save[id].front(); Save[id].pop();
Que.push(Len(tem.x, tem.y, 0)); vis[tem.x][tem.y] = true;
}
while (!Que.empty()) {
Len tem = Que.front(); Que.pop();
for (int i = 0; i < 4; ++i) {
int Nx = tem.x+Mx[i];
int Ny = tem.y+My[i];
if (!type[Nx][Ny] or vis[Nx][Ny]) continue;
if (idx[Nx][Ny]) { smin(len[id][idx[Nx][Ny]], tem.l); continue;}
vis[Nx][Ny] = true;
Que.push(Len(Nx, Ny, tem.l+1));
}
}
}
void work () {
memset(f, 0x3f, sizeof(f));
for (int i = 1; i <= ind; ++i) {f[(1<<(i-1))][i] = 0;}
for (int x = 1; x < (1<<ind); ++x) {
for (int u = 1; u <= ind; ++u) {
if (!x&(1<<(u-1))) continue;
for (int v = 1; v <= ind; ++v) {
if (x&(1<<(v-1))) continue;
smin(f[x|(1<<(v-1))][v], f[x][u]+len[u][v]);
}
}
}
int ans = INT_MAX;
for (int i = 1; i <= ind; ++i) { smin(ans, f[(1<<ind)-1][i]);}
printf("%d", ans);
}
void prepare () {
scanf("%d %d", &r, &c);
for (int i = 1; i <= r; ++i) {
for (int j = 1; j <= c; ++j) {
int ch = getch();
if (ch == ‘X‘) type[i][j] = 1;
if (ch == ‘S‘) type[i][j] = 2;
}
}
for (int i = 1; i <= r; ++i) {
for (int j = 1; j <= c; ++j) {
if (!idx[i][j] and type[i][j] == 1) {
idx[i][j] = ++ind;
Getpoints(i, j);
}
}
} //prepare for the distance
memset(len, 0x3f, sizeof(len));
for (int i = 1; i <= r; ++i) {
for (int j = 1; j <= c; ++j) {
if (idx[i][j] and !hasgone[idx[i][j]]) {
memset(vis, 0, sizeof(vis));
hasgone[idx[i][j]] = true;
Getlen(idx[i][j]);
}
}
} //Get the cloest distance
for (int i = 1; i <= ind; ++i) {
for (int j = i+1; j <= ind; ++j) {
for (int k = i+1; k < j; ++k) {
smin(len[i][j], len[i][k]+len[k][j]);
len[j][i] = len[i][j];
}
}
} //floyd Part
work();
} int main () { prepare();}
```
原文地址:https://www.cnblogs.com/NHDR233/p/11246717.html