您的位置:首页 > 其它

「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问题了。

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: