[BZOJ4006][[JLOI2015]管道连接][斯坦纳树+状压DP+SPFA]

50 篇文章 0 订阅
2 篇文章 0 订阅

[BZOJ4006][[JLOI2015]管道连接][斯坦纳树+状压DP+SPFA]

题目懒得写了。。就是斯坦纳树裸题

思路:

由于需要链接起来的点集最多只有10个,所以可以把集合的状态状压一下。然后设 f[i][S] 为点 i S点集中的最小花费,则有转移:

f[i][S]=min{f[i][S]+f[i][SS]}

f[i][S]=min{f[i][S]+v(i,i)}

这样做两次状压DP,有一次用SPFA维护就好了。

代码:
#include <bits/stdc++.h>
using namespace std;
const int Maxn = 1005, Maxm = 6005, INF = 0x3f3f3f3f;

inline char get(void) {
    static char buf[100000], *p1 = buf, *p2 = buf;
    if (p1 == p2) {
        p2 = (p1 = buf) + fread(buf, 1, 100000, stdin);
        if (p1 == p2) return EOF;
    }
    return *p1++;
}

inline void read(int &x) {
    x = 0; static char c;
    for (; !(c >= '0' && c <= '9'); c = get());
    for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get());
}
struct Re {
    int x, id;
    friend bool operator < (const Re &a, const Re &b) {
        return a.x < b.x;
    }
} re[20];

int head[Maxn], sub, bin[50], n, m , p, f[1 << 10][Maxn], g[1 << 10];
bool vis[Maxn];
struct Edge {
    int to, nxt, v;
    Edge(void) {}
    Edge(const int &to, const int &nxt, const int &v) : to(to), nxt(nxt), v(v) {}
} edge[Maxm];

inline void add(int a, int b, int v) {
    edge[++sub] = Edge(b, head[a], v), head[a] = sub;
}

inline int Min(const int &a, const int &b) {
    return a < b ? a : b;
}
inline void solve(int now, int cnt) {
    for (int k = 1; k < bin[cnt]; k++) {
        for (int i = 1; i <= n; i++) {
            queue<int> qu;
            memset(vis, 0, sizeof vis);
            for (int j = (k - 1) & k; j; j = (j - 1) & k)
                f[k][i] = Min(f[k][i], f[j][i] + f[j ^ k][i]);
            if (f[k][i] != INF) qu.push(i), vis[i] = 1;
            while (!qu.empty()) {
                int u = qu.front(); qu.pop(); vis[u] = 0;
                for (int i = head[u], v; i; i = edge[i].nxt) {
                    v = edge[i].to;
                    if (f[k][u] + edge[i].v < f[k][v]) {
                        f[k][v] = f[k][u] + edge[i].v;
                        if (!vis[v]) {
                            vis[v] = 1;
                            qu.push(v);
                        }
                    }
                }
            }
        }
    }
    g[now] = INF; for (int i = 1; i <= n; i++) g[now] = Min(g[now], f[bin[cnt] - 1][i]);
}

int main(void) {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);

    read(n), read(m), read(p);
    bin[0] = 1; for (int i = 1; i < 20; i++) bin[i] = bin[i - 1] << 1;
    for (int i = 1, u, v, w; i <= m; i++) {
        read(u), read(v), read(w);
        add(u, v, w); add(v, u, w);
    }
    for (int i = 1; i <= p; i++) read(re[i].x), read(re[i].id);
    sort(re + 1, re + p + 1);
    int last = -1, cnt = 0;
    for (int i = 1; i <= p; i++) {
        if (re[i].x != last) last = re[i].x, cnt++;
        re[i].x = cnt;
    }
    for (int i = 1; i < bin[cnt]; i++) g[i] = INF;
    for (int k = 1; k < bin[cnt]; k++) {
        int tmp = 0;
        for (int i = 1; i <= p; i++) if (k & bin[re[i].x - 1]) tmp++;
        for (int i = 1; i < bin[tmp]; i++)
            for (int j = 1; j <= n; j++) f[i][j] = INF;
        tmp = 0;
        for (int i = 1; i <= p; i++)
            if (k & bin[re[i].x - 1]) {
                f[bin[tmp]][re[i].id] = 0;
                tmp++;
            }
        solve(k, tmp);
        for (int i = (k - 1) & k; i; i = (i - 1) & k) g[k] = Min(g[k], g[i] + g[i ^ k]);
    }
    printf("%d\n", g[bin[cnt] - 1]);
    return 0;
} 

完。

By g1n0st

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值