WC2008观光游览【BZOJ2595】【斯坦纳树】

来源:互联网 发布:大专学软件开发 编辑:程序博客网 时间:2024/04/28 07:06

WC2008观光游览【BZOJ2595】【斯坦纳树】

神奇的解法


题目传送点

想了解斯坦纳树的戳这


其实这种表格的题目还可以写插头DP(•‾̑⌣‾̑•)✧˖°
(不会= =|||)
我们忽略刚刚的话题,说说这个斯坦纳树。

《斯坦纳树问题及其推广》说道:

斯坦纳树问题属于NP困难问题,因此看来不可能出现有效算法,即所谓的多项式算法来处理这一问题。目前文献中所提出的算法只不过是遍数法的种种改进,即在具体计算过程中,根据给定的实际问题,利用某些准则可以抛弃一部分情况不予考虑

也就是说,大部分的斯坦纳树问题都是构造的,没有一个特定的解法,我们学长还和我们抱怨说:考一些莫名其妙的题┑( ̄▽  ̄)┍(闲话不多说,题解上)

题解

[纯暴力]

太暴力了这种办法,枚举每一个不是风景的格子按不安排志愿者O((NM)!)的复杂度,(我当时做练习就是用的这种),只有二十分

[YY]

SPFADijkstra, Floyd,对每两个点之间做一次最短路,再求一个最小生成树,但是这钟算法很好卡,当时就没写,结果有60分,( `)3’)▃▃▃▅▆▇▉

[插头DP]

不会。。。。

[斯坦纳树]

这个才是重点。
我们依然定义一个状态f[i][j][s]表示一棵树(显然,题目要求的那条路径一定是一棵树),以(i,j)为根,风景选择的集合为s(最多只有十个风景,用二进制来表示哪个点选了)的最短路径是多长,于是有下面的转移:

  • f[i][j][s]=f[i][j][s]+f[i][j][ss]val(i,j)(ss)(两棵树的合并,会有一个点算重了,要减掉,枚举s的时候,s不能为,也不能等于s
  • f[i][j][s]=min(f[x][y][s]+val(i,j))((i,j)从周围四个点转移过来,当然,如果(i,j)是个景点,那么,s要比s多一个点)

预处理:
首先f[i][j][s]赋值为最大值INF,在读取每个点的权值的时候,f[i][j][]=val(i,j),如果(i,j)是景点f[i][j][(i,j)]=0,因为第二个转移很像SPFA里面的松弛操作,我们就可以采用SPFA的办法来转移(其实个人觉得第一个转移像Floyd)。

[代码]

(大家帮我向CSDN提个建议✧ (≖ ‿ ≖)✧,markdown的插入代码的格式太丑了,没有html的代码块那么好看,但是markdown好用些)

#include <iostream>#include <cstdlib>#include <cstdio>#include <queue>#include <cstring>using namespace std;const int maxn = 13, maxm = 1035, INF = 0x7fffffff / 3, bai = 102501, shi = 1024;const int dirx[] = {0, 0, 1, -1}, diry[] = {1, -1, 0, 0};int n, m, k, M, map[maxn][maxn], f[maxn][maxn][maxm], fa[maxn][maxn][maxm], ord[maxn][maxn];bool used[1200130], ans[maxn][maxn];queue<int>Q;int calc(int x, int y, int s) {return x * bai + y * shi + s;}bool isSubet(int a, int b) {return (a | b) == a;}void spfa(int sta) {    while(!Q.empty()) {        int u = Q.front(); Q.pop();        int x = u / bai, y = u % bai / shi;        for(int d = 0; d < 4; d++) {            int nx = x + dirx[d], ny = y + diry[d];            if(nx < 1 || ny < 1 || nx > n || ny > m) continue;            int ns = sta | ord[nx][ny];            if(f[nx][ny][ns] > f[x][y][sta] + map[nx][ny]) {                f[nx][ny][ns] = f[x][y][sta] + map[nx][ny];                fa[nx][ny][ns] = u;                int k = calc(nx, ny, ns);                if(!used[k] && sta == ns) {used[k] = true; Q.push(k);}            }        }        used[u] = false;    }}void initForOut(int x, int y, int sta) {    ans[x][y] = true;    int k = fa[x][y][sta];    if(!k) return;    int i = k / bai, j = k % bai / shi, s = k % bai % shi;    initForOut(i, j, s);    if(i == x && j == y) initForOut(i, j, sta - s);}void out() {    for(int i = 1; i <= n; i++) {        for(int j = 1; j <= m; j++)            if(ans[i][j])                if(map[i][j]) putchar('o');                else putchar('x');            else putchar('_');        putchar('\n');    }}int main() {    freopen("trip.in", "r", stdin);    freopen("trip.out", "w", stdout);    scanf("%d%d", &n, &m);    for(int i = 1; i <= n; i++)        for(int j = 1; j <= m; j++)            for(int s = 0; s < 1024; s++) f[i][j][s] = INF;    for(int i = 1; i <= n; i++)        for(int j = 1; j <= m; j++) {            scanf("%d", &map[i][j]);            f[i][j][0] = map[i][j];            if(!map[i][j]) f[i][j][1 << (k++)] = 0, ord[i][j] = 1 << (k - 1);        }    M = 1 << k;    for(int sta = 1; sta < M; sta++) {        for(int i = 1; i <= n; i++) {            for(int j = 1; j <= m; j++) {                for(int s = 1; s < sta; s++) {                    if(isSubet(sta, s)) {                        if(f[i][j][sta] > f[i][j][s] + f[i][j][sta - s] - map[i][j]) {                            f[i][j][sta] = f[i][j][s] + f[i][j][sta - s] - map[i][j];                            fa[i][j][sta] = calc(i, j, s);                        }                    }                }                if(f[i][j][sta] != INF) Q.push(calc(i, j, sta)), used[calc(i, j, sta)] = true;            }        }        spfa(sta);    }    for(int i = 1; i <= n; i++)        for(int j = 1; j <= m; j++)            if(!map[i][j]) {                printf("%d\n", f[i][j][M - 1]);                initForOut(i, j, M - 1);                out();                return 0;            }    return 0;}
1 0
原创粉丝点击