题解 P3070 [USACO13JAN]岛游记Island Travels
Posted nhdr233
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 P3070 [USACO13JAN]岛游记Island Travels相关的知识,希望对你有一定的参考价值。
题面有点坑,翻译内容中没有指明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();
```
以上是关于题解 P3070 [USACO13JAN]岛游记Island Travels的主要内容,如果未能解决你的问题,请参考以下文章
洛谷 P2205 [USACO13JAN]画栅栏Painting the Fence
题解 P3608 [USACO17JAN]Balanced Photo平衡的照片
题解 P6004 [USACO20JAN]Wormhole Sort S
题解 P2937 [USACO09JAN]激光电话Laserphones