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预处理替代,第二次更新时直接用最短路来更新就好了。
代码
//状态压缩型动态规划 #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;}
- codevs2800送外卖题解
- Codevs2800 送外卖
- [CodeVS2800] 送外卖
- codevs2800送外卖
- CodeVS2800 送外卖
- 【codevs2800】送外卖 floyd+状压DP
- 【codevs2800】送外卖,状态压缩DP练习
- [CODEVS2800]送外卖(状压dp)
- codevs2800送外卖(floyd+状压dp)
- [codevs2800]送外卖(状压dp)
- 送外卖
- 送外卖
- wikioi 2800 送外卖
- wikioi 送外卖
- 【wikioi2800】送外卖
- covs 2800 送外卖
- Codevs2880 送外卖
- CODEVS 2800 送外卖
- 用方法论学Java 之——第二章:Java 系统框架及特性
- 处理排序空值
- 这12款开源数据分析应用软件值得关注
- hdu 1494 跑跑卡丁车 dp
- Linux网络编程:网络服务器的分类及运用
- CodeVS2800 送外卖
- “Dirty COW”提权漏洞!CentOS 7发布内核修复补丁
- printf函数,fprintf函数,sprintf函数
- PCL中Sample_consensus模块支持的几何模型
- Reorder the Books hdu 5500 贪心
- 二叉树的遍历
- 脚本安装php5.5+mysql+apache+phpmyadmin
- linux C学习一站式学习资料链接
- Mysql的with rollup(5.1以上版本)