「Poetize9」礼物运送 Tyvj 2033 DP
2015-10-22 21:51
381 查看
看到这道题的第一眼,搜索搜索。觉得n只有18除了搜索也没谁了。于是很快写出了搜索方法:sec(a, b, ta, tb, ed)当第一个人在a,已经用时ta,第二个人在b,已经用时tb,两人一共经过了ed个点。floyd预处理出每两个点之间最短路,每次枚举下一个被访问的点,以及被谁访问。华丽丽的阶乘级搜索,数据范围小到18也是不行,自己还脑残的地以为,优化优化。
最终还是放弃了挣扎,又突然想到了一个词:记忆化。n只有18,可以压缩状态。于是想,怎么开数组呢,自己的搜索状态是5个。f[i][j][k]表示两个人分别在i,j,已经访问过的点的集合石k时,还需要最短时间?然而这个并不好表达我们需要的“记忆化”所达到的“剪枝”效果。因为这道题,时间是两个中的max,只记录一个肯定是狭隘的做法。
于是想,怎么表达(转移)呢?要想好转移,如果只表示一个人就好说多了,f[i][j]表示这个人在i,已经访问的集合为j时,还需要最少的时间来访问所有点。f[i][j] = min{f[k][j+k]+w[i][k]}(当然还需要floyd的预处理)。之后我们花2^n的时间枚举第一个人访问哪些点,那么剩下的点就是另一个人需要访问的,而这两个就是f[i][j]的两种状态。于是问题就解决了。把阶乘级的搜索变成了指数级状压DP。
注意:因为两个人都是从1出发,所以上述的“两个状态”是f[1][k], f[1][t]。就是说,我们需要:当前在点1,已经走过的点的集合是i时,还需要的最少时间。所以记忆化搜索就不能从d(1,1)开始,那样得不到f[1][i]。
这道题的DP过程就是小数据范围的TSP问题了。
最终还是放弃了挣扎,又突然想到了一个词:记忆化。n只有18,可以压缩状态。于是想,怎么开数组呢,自己的搜索状态是5个。f[i][j][k]表示两个人分别在i,j,已经访问过的点的集合石k时,还需要最短时间?然而这个并不好表达我们需要的“记忆化”所达到的“剪枝”效果。因为这道题,时间是两个中的max,只记录一个肯定是狭隘的做法。
于是想,怎么表达(转移)呢?要想好转移,如果只表示一个人就好说多了,f[i][j]表示这个人在i,已经访问的集合为j时,还需要最少的时间来访问所有点。f[i][j] = min{f[k][j+k]+w[i][k]}(当然还需要floyd的预处理)。之后我们花2^n的时间枚举第一个人访问哪些点,那么剩下的点就是另一个人需要访问的,而这两个就是f[i][j]的两种状态。于是问题就解决了。把阶乘级的搜索变成了指数级状压DP。
注意:因为两个人都是从1出发,所以上述的“两个状态”是f[1][k], f[1][t]。就是说,我们需要:当前在点1,已经走过的点的集合是i时,还需要的最少时间。所以记忆化搜索就不能从d(1,1)开始,那样得不到f[1][i]。
这道题的DP过程就是小数据范围的TSP问题了。
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> using namespace std; int n, m, M, ans = (1<<30), w[20][20], f[20][(1<<18)+100]; int d(int i, int j) { if(f[i][j] || j == M) return f[i][j]; f[i][j] = 1 << 30; for(int k = 1; k <= n; k++) { int p = k-1; if(j>>p&1) continue; f[i][j] = min(f[i][j], d(k, j+(1<<p))+w[i][k]); } return f[i][j]; } int main() { scanf("%d %d", &n, &m); M = (1<<n)-1; memset(w, 0x3f, sizeof w); for(int i = 1; i <= m; i++) { int a, b, c; scanf("%d %d %d", &a, &b, &c); w[a][b] = w[b][a] = c; } for(int k = 1; k <= n; k++) for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { w[i][j] = min(w[i][j], w[i][k]+w[k][j]); } for(int i = 1; i <= n; i++) w[i][0] = w[0][i] = 0; d(0, 0); for(int i = 1; i <= M; i++) { int a = i|1, b = i^M|1; ans = min(ans, max(f[1][a], f[1][b])); } printf("%d", ans); return 0; }
相关文章推荐
- Java访问权限修饰词private、public、protected、default、default(缺省)
- leetcode--single number
- Could not load file or assembly 'Microsoft.VisualStudio.Web.PageInspector.Loader
- jsp之建立一个九九乘法表
- 第四次作业——个人作业——软件案例分析
- Android 高级UI设计笔记04:可以拖动交换item位置的GridView ---> 使用setDrawingCacheEnabled(boolean flag)提高绘图速度
- zigbee项目之上位机app远程控制终端
- 运动捕捉数据的描述ASF/AMC
- week7---10月23日 DIV布局定位(一)
- KNN
- POJ 3259——Wormholes——————【最短路、SPFA、判负环】
- 词法分析编译感想
- VIM常用命令总结
- ural 1150. Page Numbers
- 如何做当当网购物结账页面?
- JavaScript之DOM对象和内置对象
- poj2828 Buy Tickets
- Ubuntu14.04更改为NVIDIA显卡驱动之后黑屏的问题
- EF-实体框架和Linq(2)
- 打破CA和电子签章厂家的技术垄断的一个想法