题目大意: 自己选择位置进入迷宫,选最好的路径访问所有的宝藏,然后走出迷宫。要求最短”时间“
首先,这是一个迷宫问题。但是普通的dfs,bfs 并不能求出访问所有宝藏的时间总和。
所以,可以再抽象一下,求出宝藏两两间的距离当成边,宝藏当成顶点,然后构图。最短路? 不,最短路不能求和。
尝试dp ,那种dp ? (1<=k<=13 )状态压缩?
想想以前做过的经典题——TSP 旅行商问题。
当是 旅行商问题 求解的是访问所有节点一次,然后回到原点??
这里,并不要求,访问完所有宝藏之后回到第一个访问的宝藏。
但是,进入迷宫和走出迷宫也是需要考虑的。
idea——-干脆再加入一个顶点,编号为0,代表迷宫外。
然后就构成旅行商问题的裸体的。
算法实现:
1 构图 : 枚举每个顶点 i,求出这个顶点距离地图外的最短时间,记录为i到0号顶点的距离。 求出宝藏两两之间的距离。 以邻接矩阵形式储存。
2 跑一遍状态压缩dp 。
另外: 传说旅行商问题有 O(n*(2^n))的写法,但是不知为何wrong 了。。下面给出挑战上 O(n*n*(2^n))的写法.
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
int n,m,k;
int isin[205][205];
bool inque[205][205];
int xx[15],yy[15]; //珍宝的位置
int map1[202][202]; //地图上的权值
int d[202][202]; //广搜的距离数组
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}}; //广搜的方向
const int INF=200*200*10*200;
typedef pair<int ,int > P;
int dist[15][15];
int dp[1<<15][15];
int bfs(int sx,int sy,int oo)
{
memset(inque,0,sizeof(inque));
queue<P> que;
while(!que.empty())
{ que.pop();
}
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
d[i][j]=INF;
d[sx][sy]=0;
inque[sx][sy]=true;
que.push(P(sx,sy));
int mi=INF;
while(!que.empty())
{
P p=que.front(); que.pop();
inque[p.first][p.second]=false;
for(int i=0;i<4;i++)
{
int nx=p.first +dir[i][0],ny=p.second +dir[i][1];
if(nx>=0&&nx<n && ny>=0&&ny<m)
{
if(map1[nx][ny]!=-1 &&d[p.first ][p.second ]+map1[nx][ny]<d[nx][ny])
{
if(isin[nx][ny]>0)
{
if(d[p.first ][p.second ]<dist[isin[nx][ny]][oo])
{
dist[oo][isin[nx][ny]]= dist[isin[nx][ny]][oo]=d[p.first ][p.second ];
}
}
d[nx][ny]=d[p.first ][p.second]+map1[nx][ny];
if(inque[nx][ny]==false)
{
que.push(P(nx,ny));
inque[nx][ny]=true;
}
}
}
else
{
if(d[p.first ][p.second]<mi)
mi=d[p.first ][p.second];
}
}
}
return mi;
}
// wrong answer 的写法。
/*void solve()
{
k=k+1;
int num1=1<<k;
for(int s=0;s<num1;s++)
{
fill(dp[s],dp[s]+k,INF);
}
for(int i=0;i<k;i++)
{
dp[1<<i][i]=dist[i][0];
}
int num3=1<<k;
for(int s=0;s<num3;s++){
for(int i=0;i<k;i++){
if(s&(1<<i)==0)continue;
if(dp[s][i]==INF)continue;
for(int j=0;j<k;j++){
if(s&(1<<j)==1)continue;
dp[s|(1<<j)][j]=min(dp[s|(1<<j)][j],dp[s][i]+dist[i][j]);
}
}
}
int res=dp[(1<<k)-1][0];
for(int i=1;i<=k-1;i++)
{
res+=map1[xx[i]][yy[i]];
}
printf("%d\n",res);
} */
/* int rec(int s,int v)
{
if(dp[s][v]>=0) return dp[s][v];
if(s==(1<<k)-1&&v==0)
{
return dp[s][v]=0;
}
int res=INF;
for(int u=0;u<k;u++)
{
if(!(s>>u&1))
{
res=min(res,rec(s|1<<u,u)+dist[v][u]);
}
}
return dp[s][v]=res;
}*/
void solve1()
{
k=k+1;
int num1=1<<k;
for(int s=0;s<num1;s++)
{
fill(dp[s],dp[s]+k,INF);
}
// printf("%d %d\n",dist[1][0],dist[0][1]);
dp[(1<<k)-1][0]=0;
for(int s=(1<<k)-2;s>=0;s--)
{
for(int v=0;v<k;v++)
{
for(int u=0;u<k;u++)
{
if(!(s>>u&1))
{
dp[s][v]=min(dp[s][v],dp[s|1<<u][u]+dist[v][u]);
}
}
}
}
int res=dp[0][0];
for(int i=1;i<=k-1;i++)
{
res+=map1[xx[i]][yy[i]];
}
printf("%d\n",res);
}
int main()
{
// freopen("F:\\123.txt","r",stdin);
int t;
scanf("%d",&t);
for(int ik=1;ik<=t;ik++)
{
memset(isin,0,sizeof(isin));
scanf("%d%d",&n,&m);
for(int i=0;i<=15;i++)
for(int j=0;j<=15;j++)
dist[i][j]=INF;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
scanf("%d",&map1[i][j]);
}
}
scanf("%d",&k);
for(int i=1;i<=k;i++)
{
scanf("%d%d",&xx[i],&yy[i]);
isin[xx[i]][yy[i]]=i;
}
int sx,sy,gx,gy;
for(int i=1;i<=k;i++)
{
dist[i][0]= dist[0][i]=bfs(xx[i],yy[i],i);
}
solve1();
/* k=k+1;
int num=1<<k;
for(int i=0;i<num;i++)
{
for(int j=0;j<20;j++)
{
dp[i][j]=-1;
}
}
int res=rec(0,0);
for(int i=1;i<=k-1;i++)
{
res+=map1[xx[i]][yy[i]];
}
printf("%d\n",res); */
}
return 0;
}