CodeVS2800 送外卖

来源:互联网 发布:软件服务器租用 编辑:程序博客网 时间:2024/06/11 12:47

题目

  http://codevs.cn/problem/2800/

题解

  这是状态压缩型动态规划。

  mdzz做了一节晚自习结果1A了好爽qaq

  第一次做状压感觉有点费劲。。。

  状态很好设计:f[i][j]表示在i情形下,当前所在城市为j的最小时间。i是一个二进制数,有N+1位,每一位为0或1代表了这个城市是否被送过外卖。

  很明显目标状态是f[(1<<(N+1))-1][0],就是说全都送完了并且最后在0这个点的最短时间。

  考虑状态f[i][j],首先考虑我是第一次来j这个点的情况,那么就要在i中抠去1<<j,令t=i-(1<<j),用剩余的1转移f[i][j],就是这个意思:对于t中每一个1所对应的点的编号k,用f[t][k]+dist[k][j]去更新f[i][j]。这样得到的f[i][j]是说我仅仅最后一步走到了j这个点,之前一直没有走过。

  根据题目要求,一个点可能被走多次。考虑一个点被走了多次的情况。那么你就要用一个第j位是1的状态来更新f[i][j],即是说用f[i][k]+dist[k][j]更新f[i][j]。在做这次更新之前,你的f[i][j]存的应该是最后一步走到j且之前没有来过j的情况,那么用f[i][k]+dist[k][j]更新f[i][j]的意义就是仍然在i情形下,又从j出发走了一步到已走过的点。根据题意,可能存在如下情形:


  明显地,走到2之后,你要走两步到0,如果仅转移一次的话,答案就成了102,而实际是4。也就是说我们应该做N次这样的同情形下的转移。

(后来补上的:)

  其实上面最后讨论的那个走多次的问题,让复杂度多一个N。而这个其实可以用一个floyd预处理替代,第二次更新时直接用最短路来更新就好了。

代码

这是纯状压DP(2533ms)
//状态压缩型动态规划 #include <cstdio>#include <algorithm>#include <cstring>#define maxn 17#define inf 0x3f3f3f3fusing namespace std;int f[1<<maxn][maxn], N, map[maxn][maxn];void input(){int i, j;scanf("%d",&N);for(i=0;i<=N;i++)for(j=0;j<=N;j++)scanf("%d",map[i]+j);}int dp(){int i, j, k, ans=inf, ii;memset(f,inf,sizeof(f));f[1][0]=0;for(i=1;i<(1<<N+1);i++){for(j=0;j<=N;j++)if(i&(1<<j))for(k=0;k<=N;k++)if(i&(1<<k))f[i][j]=min(f[i][j],f[i&~(1<<j)][k]+map[k][j]);for(ii=1;ii<=N;ii++)for(j=0;j<=N;j++)if(i&(1<<j))for(k=0;k<=N;k++)if(i&(1<<k))f[i][j]=min(f[i][j],f[i][k]+map[k][j]);}return f[(1<<(N+1))-1][0];}int main(){input();printf("%d",dp());return 0;}
这是Floyd+状压DP(487ms)
//fpoyd+状压DP #include <cstdio>#include <algorithm>#include <cstring>#define maxn 17#define inf 0x3f3f3f3fusing namespace std;int f[1<<maxn][maxn], N, dist[maxn][maxn];void input(){int i, j;scanf("%d",&N);for(i=0;i<=N;i++)for(j=0;j<=N;j++)scanf("%d",dist[i]+j);}void floyd(){int i, j, k;for(k=0;k<=N;k++)for(i=0;i<=N;i++)for(j=0;j<=N;j++)dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);}int dp(){int i, j, k;memset(f,inf,sizeof(f));f[0][0]=0;for(i=1;i<(1<<(N+1));i++){for(j=0;j<=N;j++)if(i&(1<<j))for(k=0;k<=N;k++)if(i&(1<<j))f[i][j]=min(f[i][j],f[i&~(1<<j)][k]+dist[k][j]);for(j=0;j<=N;j++)if(i&(1<<j))for(k=0;k<=N;k++)if(i&(1<<k))f[i][j]=min(f[i][j],f[i][k]+dist[k][j]);}return f[(1<<(N+1))-1][0];}int main(){input();floyd();printf("%d",dp());return 0;}


0 0