题目
比赛
题意:
一个n×m的网格图,每个格子有一个数字,表示从这个格子出发到相邻格子所需时间,-1表示不能经过。有一些格子上有宝藏,求拿完所有能拿的宝藏所需的最少时间。
题解:
由于宝藏很少,所以可以状压。首先以每个宝藏为起点做一次SPFA,就可以得到两个宝藏间的最短路(单向),还有离开网格图的最短路(如果不能离开就是不能取)。然后状压,dp[i][j]表示最后选的宝藏是i,j表示已经取的宝藏。复杂度是O(13*13*2^13)。
//Time:187ms //Memory:1284KB //Length:2965B #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <queue> #include <vector> #define MAXN 210 #define MP(x,y) make_pair((x),(y)) #define FI first #define SE second #define INF 10000007 using namespace std; pair<int,int> pa[20]; const int dir[4][2]={{0,1},{1,0},{-1,0},{0,-1}}; int ma[MAXN][MAXN]; bool vi[MAXN][MAXN],can[20],tvi[13][1<<13]; int len[MAXN][MAXN],ans; int to[20][20],out[20]; int dp[13][1<<13]; int n,m,k; void bfs(int x,int y) { priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > que; memset(vi,0,sizeof(vi)); memset(len,0x3f,sizeof(len)); que.push(MP(0,x*m+y)); len[x][y]=0; while(que.size()) { x=que.top().SE/m,y=que.top().SE%m; que.pop(); if(vi[x][y]) continue; vi[x][y]=1; for(int i=0;i<4;++i) { int tx=x+dir[i][0],ty=y+dir[i][1]; if(tx<0||tx>=n||ty<0||ty>=m) continue; if(len[tx][ty]>len[x][y]+ma[tx][ty]&&ma[tx][ty]>=0) len[tx][ty]=len[x][y]+ma[tx][ty], que.push(MP(len[tx][ty],tx*m+ty)); } } } void solve () { int top=(1<<k)-1; for(int i=0;i<k;++i) if(can[i]) top^=1<<i; queue<int>que; memset(dp,0x3f,sizeof(dp)); memset(tvi,0,sizeof(tvi)); for(int i=0;i<k;++i) dp[i][1<<i]=out[i]+ma[pa[i].FI][pa[i].SE],que.push(i*INF+(1<<i)); while(que.size()) { int s=que.front()/INF,t=que.front()%INF; que.pop(); if(t==top) { ans=min(ans,dp[s][t]+out[s]); continue; } for(int i=0;i<k;++i) if((1<<i)&t) continue; else { if(dp[i][t|(1<<i)]>dp[s][t]+to[s][i]) { dp[i][t|(1<<i)]=dp[s][t]+to[s][i]; if(!tvi[i][t|(1<<i)]) tvi[i][t|(1<<i)]=1,que.push(i*INF+(t|(1<<i))); } } } } int main() { //freopen("/home/moor/Code/input.txt","r",stdin); int ncase; scanf("%d",&ncase); while(ncase--) { scanf("%d%d",&n,&m); for(int i=0;i<n;++i) for(int j=0;j<m;++j) scanf("%d",&ma[i][j]); scanf("%d",&k); for(int i=0;i<k;++i) scanf("%d%d",&pa[i].FI,&pa[i].SE); memset(can,0,sizeof(can)); for(int i=0;i<k;++i) { bfs(pa[i].FI,pa[i].SE); out[i]=INF; for(int j=0;j<k;++j) to[i][j]=len[pa[j].FI][pa[j].SE]; for(int j=0;j<m;++j) out[i]=min(out[i],len[0][j]),out[i]=min(out[i],len[n-1][j]); for(int j=0;j<n;++j) out[i]=min(out[i],len[j][0]),out[i]=min(out[i],len[j][m-1]); if(out[i]==INF) can[i]=1; } ans=INF; solve(); printf("%d\n",ans==INF?0:ans); } return 0; }