【BZOJ 3049】【USACO2013 Jan】Island Travels BFS+状压DP
这是今天下午的互测题,只得了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写的就是这么丑,这又怎样?
NOI 2017 Bless All