题意:给出一个二维宝藏图,有些点为-1代表不能走,否则的话代表挖这个点的宝藏需要的花费。然后给出k个点,问经过出边界出发经过这k个点,再从边界结束的最小花费是多少?从哪个边界出发和结束不限制。
思路:
首先想到的就是tsp问题,不过这里首先要建边,求出这k个点到各个点的距离和到边界的最小距离。然后跑tsp的状压dp.dp[i][s]代表这个跑完s集合以i结束的最小花费,那么当j也属于这个集合的时候,那么除了i的集合以j结束的状态就可以更新dp[i][s],状态转移方程为dp[i][s] = minI(dp[i][s],dp[j][s ^(1 <<(j - 1))]));
PS :输入的时候可以以线性输入的,现在二维输入的,搞得代码有点丑。
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> P;
#define clr(x,y) memset(x,y,sizeof x)
const int maxn = 200 + 10;
#define INF 0x3f3f3f3f
int maps[maxn][maxn];
P a[maxn];
int dist[20][maxn * maxn];
int head[maxn * maxn * 10];
struct Edge
{
Edge(){}
Edge(int x,int y,int z):to(x),w(y),next(z){}
int to,w,next;
}edge[maxn * maxn * 10];
int edge_num;
int dp[20][1 << 15];
int boundary[maxn * maxn];
int d[maxn];
void Init()
{
clr(head,-1);
edge_num = 0;
}
void add_edge(int x,int y,int z)
{
edge[edge_num] = Edge(y,z,head[x]);
head[x] = edge_num ++;
}
int dijkstra(int s,int pos)
{
priority_queue<P,vector<P>,greater<P> >q;
dist[pos][s] = 0;
q.push(P(0,s));
while(!q.empty())
{
P t = q.top();q.pop();
int u = t.second;
if(t.first > dist[pos][u])
continue;
for(int i = head[u]; i != -1;i = edge[i].next)
{
int v = edge[i].to,w = edge[i].w;
if(dist[pos][v] > dist[pos][u] + w)
{
dist[pos][v] = dist[pos][u] + w;
q.push(P(dist[pos][v],v));
}
}
}
}
int main()
{
int Tcase;
scanf("%d",&Tcase);
while(Tcase --)
{
Init();
int n,m;
scanf("%d%d",&n,&m);
for(int i = 0; i < n; i ++)
for(int j = 0; j < m; j ++)
scanf("%d",&maps[i][j]);
int len = 0;
for(int i = 0; i < m; i ++)
{
boundary[len ++] = i;boundary[len ++] = (n - 1) * m + i;
}
for(int i = 0; i < n; i ++)
{
boundary[len ++] = i * m;boundary[len ++] = i * m + m - 1;
}
sort(boundary,boundary + len);
len = unique(boundary,boundary + len) - boundary;
for(int i = 0; i < n; i ++)
{
for(int j = 0; j < m; j ++)
{
int temp = i * m + j;
if(maps[i][j] != -1)
{
if(j > 0 && maps[i][j - 1] != -1)
add_edge(temp,temp - 1,maps[i][j - 1]);
if(j + 1 < m && maps[i][j + 1] != -1)
add_edge(temp,temp + 1,maps[i][j + 1]);
if(i > 0 && maps[i - 1][j] != -1)
add_edge(temp,temp - m,maps[i - 1][j]);
if(i + 1 < n&& maps[i + 1][j] != -1)
add_edge(temp,temp + m,maps[i + 1][j]);
}
}
}
int k;
scanf("%d",&k);
for(int i = 1; i <= k;i ++)
scanf("%d%d",&a[i].first,&a[i].second);
for(int i = 1; i <= k;i ++)
{
clr(dist[i],INF);
dijkstra(a[i].first * m + a[i].second,i);
}
for(int i = 1; i <= k; i ++)
{
d[i] = INF;
for(int j = 0; j < len; j ++)
{
if(a[i].first * m + a[i].second == boundary[j])
{
d[i] = 0;
break;
}
d[i]= min(d[i],dist[i][boundary[j]]);
}
}
clr(dp,INF);
for(int s = 0; s < 1 << k; s ++)
{
for(int i = 1; i <= k; i ++)
{
if((1 << (i - 1)) & s )
{
if((1 << (i - 1)) == s)
{
dp[i][s] = d[i] + maps[a[i].first][a[i].second];
}
for(int j = 1;j <= k; j ++)
{
if((1 << (j - 1) & s) && j != i)
{
dp[i][s] = min(dp[i][s],dp[j][s ^(1 <<(i - 1))] + dist[j][a[i].first * m + a[i].second]);
}
}
}
}
}
int ans = INF;
for(int i = 1; i <= k; i ++)
{
ans = min(ans,dp[i][(1 << k) - 1] + d[i]);
}
printf("%d\n",ans);
}
return 0;
}