一.题目链接:
HDU-4568
二.题目大意:
给一个 n*m 的地图,每个点上有一个数代表花费,-1代表无法经过,有 k 个关键点.
求从地图的任意一侧进入,遍历完所有的关键点,离开地图的最小花费.
三.分析:
设地图外为 0 号关键点,对 k + 1 个关键点分别跑 spfa,得到两两关键点的花费,之后状压即可.
关于状压状态设计,我们选取的是 dp[i][j] 代表点状态为 i,当前在 j 点.
之前把状态设计反,wa 了两晚上....
四.代码实现:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = (int)2e2;
const int N = (int)14;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)1e9 + 7;
int n, m, k;
int a[M + 5][M + 5];
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
int cnt;
int head[M * M + 5];
struct node
{
int v, nx, w;
}Edge[M * M * 4 + M * 8 + 5];
int x[N], y[N];
int cost[N][N];
int dp[1<<N][N];
void init(int n)
{
cnt = 0;
for(int i = 0; i <= n; ++i)
{
head[i] = -1;
}
}
void add(int u, int v, int w)
{
Edge[cnt].v = v;
Edge[cnt].w = w;
Edge[cnt].nx = head[u];
head[u] = cnt++;
}
int id(int i, int j)
{
return i * m + j + 1;
}
struct Graph
{
int dis[M * M + 5];
bool vis[M * M + 5];
queue <int> q;
void spfa(int s, int n)
{
for(int i = 0; i <= n; ++i) dis[i] = inf, vis[i] = 0;
q.push(s);
vis[s] = 1, dis[s] = 0;
while(!q.empty())
{
int u = q.front(); q.pop();
vis[u] = 0;
for(int i = head[u]; ~i; i = Edge[i].nx)
{
int v = Edge[i].v;
if(dis[v] > dis[u] + Edge[i].w)
{
dis[v] = dis[u] + Edge[i].w;
if(!vis[v])
{
vis[v] = 1;
q.push(v);
}
}
}
}
}
}G[N];
/**
1
2 4
1 3 8 7
1 10 7 6
5
0 0
0 3
0 1
1 2
1 0
**/
int main()
{
int T; scanf("%d", &T);
while(T--)
{
scanf("%d %d", &n, &m);
init(n * m);
for(int i = 0; i < n; ++i) for(int j = 0; j < m; ++j) scanf("%d", &a[i][j]);
for(int i = 0; i < n; ++i) for(int j = 0; j < m; ++j)
{
if(a[i][j] == -1) continue;
for(int k = 0; k < 4; ++k)
{
int x = i + dx[k], y = j + dy[k];
if(x >= 0 && x < n && y >= 0 && y < m && ~a[x][y]) add(id(i, j), id(x, y), a[x][y]);
}
}
for(int i = 0; i < n; ++i) {if(~a[i][0]) add(id(i, 0), 0, 0); if(~a[i][m - 1]) add(id(i, m - 1), 0, 0);}
for(int i = 0; i < m; ++i) {if(~a[0][i]) add(id(0, i), 0, 0); if(~a[n - 1][i]) add(id(n - 1, i), 0, 0);}
scanf("%d", &k);
for(int i = 1; i <= k; ++i) scanf("%d %d", &x[i], &y[i]), G[i].spfa(id(x[i], y[i]), n * m);
for(int i = 0; i < n; ++i) {if(~a[i][0]) add(0, id(i, 0), a[i][0]); if(~a[i][m - 1]) add(0, id(i, m - 1), a[i][m - 1]);}
for(int i = 0; i < m; ++i) {if(~a[0][i]) add(0, id(0, i), a[0][i]); if(~a[n - 1][i]) add(0, id(n - 1, i), a[n - 1][i]);}
x[0] = 0, y[0] = -1; G[0].spfa(id(x[0], y[0]), n * m);
++k; for(int i = 0; i < k; ++i) for(int j = 0; j < k; ++j) cost[i][j] = G[i].dis[id(x[j], y[j])];
memset(dp, inf, sizeof(dp));
dp[1][0] = 0;
for(int i = 0; i < (1<<k); ++i)
{
for(int j = 0; j < k; ++j)
{
if(dp[i][j] == inf) continue;
for(int l = 0; l < k; ++l)
{
if(i & (1<<l)) continue;
dp[i|(1<<l)][l] = min(dp[i|(1<<l)][l], dp[i][j] + cost[j][l]);
}
}
}
int ans = inf;
for(int i = 0; i < k; ++i) ans = min(ans, dp[(1<<k) - 1][i] + cost[i][0]);
printf("%d\n", ans == inf ? 0 : ans);
}
return 0;
}