这是今天下午的互测题,只得了60多分
分析一下错因:
$dis[i][j]$只记录了相邻的两个岛屿之间的距离,我一开始以为可以,后来$charge$提醒我有可能会出现来回走的情况,而状压转移就一次,无法实现来回走的情况,所以加了一个类似$floyed算法$的三重循环来更新每个点的距离,然后状态转移就可以了,枚举起点和终点,最后统计答案
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char c[53][53];
int n, m, dis[18][18], belong[53][53], f[40000][18], cnt = 0, qx[2500000], qy[2500000];
int far[2500000], head, tail;
bool vis[53][53];
inline void _(int x, int y) {
belong[x][y] = cnt;
vis[x][y] = 1;
if (x > 1 && c[x - 1][y] == 'X' && !vis[x - 1][y])
_(x - 1, y);
if (y > 1 && c[x][y - 1] == 'X' && !vis[x][y - 1])
_(x, y - 1);
if (x < n && c[x + 1][y] == 'X' && !vis[x + 1][y])
_(x + 1, y);
if (y < m && c[x][y + 1] == 'X' && !vis[x][y + 1])
_(x, y + 1);
}
inline void __(int x) {
int nowx, nowy;
while (head != tail) {
++head; if ( head >= 2500000) head %= 2500000;
nowx = qx[head];
nowy = qy[head];
if (nowx > 1 && !vis[nowx - 1][nowy] && c[nowx - 1][nowy] != '.') {
if (c[nowx - 1][nowy] == 'S') {
++tail; if (tail >= 2500000) tail %= 2500000;
qx[tail] = nowx - 1;
qy[tail] = nowy;
far[tail] = far[head] + 1;
vis[nowx - 1][nowy] = 1;
} else {
dis[x][belong[nowx - 1][nowy]] = min( dis[x][belong[nowx - 1][nowy]], far[head]);
}
}
if (nowy > 1 && !vis[nowx][nowy - 1] && c[nowx][nowy - 1] != '.') {
if (c[nowx][nowy - 1] == 'S') {
++tail; if (tail >= 2500000) tail %= 2500000;
qx[tail] = nowx;
qy[tail] = nowy - 1;
far[tail] = far[head] + 1;
vis[nowx][nowy - 1] = 1;
} else {
dis[x][belong[nowx][nowy - 1]] = min( dis[x][belong[nowx][nowy - 1]], far[head]);
}
}
if (nowx < n && !vis[nowx + 1][nowy] && c[nowx + 1][nowy] != '.') {
if (c[nowx + 1][nowy] == 'S') {
++tail; if (tail >= 2500000) tail %= 2500000;
qx[tail] = nowx + 1;
qy[tail] = nowy;
far[tail] = far[head] + 1;
vis[nowx + 1][nowy] = 1;
} else {
dis[x][belong[nowx + 1][nowy]] = min( dis[x][belong[nowx + 1][nowy]], far[head]);
}
}
if (nowy < m && !vis[nowx][nowy + 1] && c[nowx][nowy + 1] != '.') {
if (c[nowx][nowy + 1] == 'S') {
++tail; if (tail >= 2500000) tail %= 2500000;
qx[tail] = nowx;
qy[tail] = nowy + 1;
far[tail] = far[head] + 1;
vis[nowx][nowy + 1] = 1;
} else {
dis[x][belong[nowx][nowy + 1]] = min( dis[x][belong[nowx][nowy + 1]], far[head]);
}
}
}
}
int main() {
scanf("%d%d\n", &n, &m);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j) {
c[i][j] = getchar();
while (c[i][j] != 'X' && c[i][j] != '.' && c[i][j] != 'S')
c[i][j] = getchar();
}
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
if (!vis[i][j] && c[i][j] == 'X') {
++cnt;
_(i, j);
}
memset(dis, 1, sizeof(dis));
for(int i = 1; i <= cnt; ++i) {
head = 0;
tail = 0;
memset(vis, 0, sizeof(vis));
for(int k = 1; k <= n; ++k)
for(int l = 1; l <= m; ++l)
if (belong[k][l] == i) {
vis[k][l] = 1;
++tail; if ( tail >= 2500000) tail %= 2500000;
qx[tail] = k;
qy[tail] = l;
far[tail] = 0;
}
__(i);
}
for(int k = 1; k <= cnt; ++k)
for(int i = 1; i <= cnt; ++i)
for(int j = 1; j <= cnt; ++j)
if (dis[i][k] + dis[k][j] < dis[i][j])
dis[i][j] = dis[i][k] + dis[k][j];
memset(f, 1, sizeof(f));
int ans = 500000, tot = (1 << cnt) - 1;
for(int i = 1; i <= cnt; ++i)
f[1 << ( i - 1)][i] = 0;
for(int i = 1; i <= tot; ++i) {
for(int j = 1; 1 << (j - 1) <= i; ++j) {
if (1 << (j - 1) & i) {
for(int k = 1; k <= cnt; ++k)
if (k != j && (1 << (k - 1) & i))
f[i][j] = min(f[i][j], f[i ^ (1 << (j - 1))][k] + dis[k][j]);
}
}
}
for(int i = 1; i <= cnt; ++i)
ans = min(ans, f[tot][i]);
printf("%d\n",ans);
return 0;
}
以后思维得更严谨才行
我的BFS写的就是这么丑,这又怎样?