这个题目题意描述不清,没有说明只能进入一次。
题目意思很好理解,不再重复。
思路也比较好想,先计算每两个宝藏区的最短路,和每个宝藏区到边界的最短路,然后dp解决。
在计算最短路的时候,用优先队列优化的Dijkstra算法。
在-1的处理上有些小技巧。
但是!但是!!,我之前的思路是dp[i]表示状态i的最小cost值,并没有经过严格证明和认真思考导致wa好多次。实际上这个思路在计算过程中会导致信息丢失,下次动键盘前一定要证明一下。。
正确的状态转移应该是用dp[i][j]表示状态i并以第j个宝藏结尾的最小cost,这样每一步计算就保存了足够多的信息了。
话说这就是所谓的旅行商问题了。
#include<cstdio> #include<cstring> #include<queue> using namespace std; int data[202][202]; int min_d[14][14],tmp_d[202][202]; int out_d[14],k,x[14],y[14]; int Gold[202][202]; int T,n,m; const int mx[4]={1,0,-1,0},my[4]={0,1,0,-1}; const int INF = 1000000; struct D{ int x,y; int d; D(int gx,int gy,int gd){ x=gx;y=gy;d=gd; } }; bool operator < (D a,D b) { return a.d>b.d; } void cal_min_d_out_d() { priority_queue<D> que; memset(min_d,0,sizeof(min_d)); for(int i=0;i<k;i++) out_d[i]=INF; for(int i=0;i<k;i++) { bool visd[202][202]; memset(visd,0,sizeof(visd)); tmp_d[x[i]][y[i]]=data[x[i]][y[i]]; que.push(D(x[i],y[i],data[x[i]][y[i]])); visd[x[i]][y[i]]=true; if(x[i]==0||x[i]==n-1||y[i]==0||y[i]==m-1) out_d[i]=0; while(!que.empty()) { D tmp = que.top(); que.pop(); int xx=tmp.x,yy=tmp.y; for(int j=0;j<4;j++) { int tx=xx+mx[j],ty=yy+my[j]; if(tx>=0&&tx<n&&ty>=0&&ty<m&&data[tx][ty]!=-1&&!visd[tx][ty]) { tmp_d[tx][ty]=tmp_d[xx][yy]+data[tx][ty]; que.push(D(tx,ty,tmp_d[tx][ty])); visd[tx][ty]=true; if(Gold[tx][ty]) min_d[i][Gold[tx][ty]-1] = tmp_d[tx][ty]-data[tx][ty]; if(tx==0||ty==0||tx==n-1||ty==m-1) out_d[i]=min(out_d[i],tmp_d[tx][ty]-data[x[i]][y[i]]); } } } } } int main() { scanf("%d",&T); while(T--) { int oo=0; scanf("%d%d",&n,&m); for(int i=0;i<n;i++) for(int j=0;j<m;j++) { scanf("%d",&data[i][j]); } scanf("%d",&k); memset(Gold,0,sizeof(Gold)); for(int i=0;i<k;i++) { scanf("%d%d",&x[i],&y[i]); if(data[x[i]][y[i]]==-1) { i--; k--; continue; } Gold[x[i]][y[i]]=i+1; } cal_min_d_out_d(); int dp[1<<k][k+1]; for(int i=0;i<(1<<k);i++) for(int j=0;j<k;j++) dp[i][j]=INF; for(int i=0;i<k;i++) { dp[1<<i][i]=out_d[i]*2+data[x[i]][y[i]]; if(dp[1<<i][i]>=INF) { dp[1<<i][i]=0; oo=(1<<i)|oo; } } for(int i=3;i<(1<<k);i++) { for(int j=0;j<k;j++) { if(i&oo) continue; if(dp[i][j]!=INF) continue; int t=1<<j,th=0; if(t&i){ int pre=i&(i^t); for(int jj=0;jj<k;jj++) { dp[i][j]=min(dp[i][j],dp[pre][jj]-out_d[jj]+out_d[j]+min_d[j][jj]); } } } } int ans=INF; for(int i=0;i<k;i++) ans=min(dp[oo^((1<<k)-1)][i],ans); printf("%d ",ans); } }