最短路 + DP
把边界看成一个点和其他treasure点构成了k+1个点,然后求出这k+!个点,任意两点之间的最短路径(因为任何一个最优方案中的边肯定是最短路径),一个图就确定了(可能两个点之间就不可到达,也不需要管,因为他们的最短路为正无穷大),我用的SPFA,用Djikstra写了超时吗,可能是哪没写好。然后可知题目让求的就是从边界这个点访问最多的点并且返回所用的最短路径。也就是经典的TSP问题的应用。先假定这k+1个点是相互联通的,不联通的暂且看做联通。长度为正无穷大。然后,求出边界点访问所有不同的集合返回时所用的最短路径。如果所求结果>=inf,就不考虑这种情况(很显然,最短的路程都为无穷大。那说明此集合本来就不联通)。然后比较得到集合中所含点最多并且所需路径最短的结果,也就是最后所求的答案。
做的时候很多细节问题考虑了很久。从中午写到晚上10点,还好一次AC。
#include <iostream> #include <cstdio> #include <cstring> #include <stack> #include <queue> using namespace std; const int maxn=200+10; const int inf=50000000; int dir[4][2]={{0,1},{0,-1},{-1,0},{1,0}}; int cost[maxn][maxn]; int dis[15][15],vis[maxn][maxn]; int tdis[maxn*maxn],inq[maxn*maxn]; int loc[15*15]; int f[15][(1<<15)],num[1<<15]; int n,m,k; queue <int> Q; void bellmanford() { int i,j; for(i=0;i<k;i++) { for(j=0;j<n*m;j++) tdis[j]=inf; memset(inq,0,sizeof(inq)); tdis[loc[i]]=0; inq[loc[i]]=1; Q.push(loc[i]);//队列中存的是更新了的点 int floc,tloc,fx,fy,tx,ty; while(!Q.empty()) { floc=Q.front(); Q.pop(); inq[floc]=0; fx=floc/m; fy=floc%m; for(j=0;j<4;j++) { tx=fx+dir[j][0]; ty=fy+dir[j][1]; tloc=tx*m+ty; if(tx>=0&&tx<n&&ty>=0&&ty<m) { if(tdis[floc]+cost[tx][ty]<tdis[tloc]) { tdis[tloc]=tdis[floc]+cost[tx][ty]; if(!inq[tloc]) { inq[tloc]=1; Q.push(tloc); } } } } } dis[i][k]=inf; for(j=0;j<n*m;j++) { tx=j/m; ty=j%m; if(vis[tx][ty]!=-1) dis[i][vis[tx][ty]]=tdis[j]; if(tx==0||tx==n-1||ty==0||ty==m-1) dis[i][k]=min(dis[i][k],tdis[j]); } dis[k][i]=dis[i][k]+cost[loc[i]/m][loc[i]%m]; } } int main() { int T; cin>>T; int i,j; num[0]=0; for(i=1;i<(1<<13);i++) num[i]=num[i>>1]+((i&1)?1:0); while(T--) { memset(vis,-1,sizeof(vis)); scanf("%d%d",&n,&m); int j; for(i=0;i<n;i++) { for(j=0;j<m;j++) { scanf("%d",&cost[i][j]); if(cost[i][j]==-1) cost[i][j]=inf; } } scanf("%d",&k); int x,y; for(i=0;i<k;i++) { scanf("%d%d",&x,&y); vis[x][y]=i; loc[i]=x*m+y; } bellmanford(); dis[k][k]=0; for(i=0;i<=k;i++) f[i][0]=dis[i][k]; int w; int amount=0,ans=inf; for(i=1;i<(1<<k);i++) { for(j=0;j<=k;j++) { if(((1<<j)&i)==0) { f[j][i]=inf; for(w=0;w<k;w++) { if((1<<w)&i) { f[j][i]=min(f[j][i],f[w][i^(1<<w)]+dis[j][w]); } } } } if(f[k][i]<inf&&num[i]>=amount) { if(num[i]>amount) { ans=f[k][i]; amount=num[i]; } else if(f[k][i]<ans) ans=f[k][i]; } } printf("%d\n",ans); } return 0; }