[BZOJ4006][[JLOI2015]管道连接][斯坦纳树+状压DP+SPFA]
题目懒得写了。。就是斯坦纳树裸题
思路:
由于需要链接起来的点集最多只有10个,所以可以把集合的状态状压一下。然后设
f[i][S]
为点
i
在
f[i][S]=min{f[i][S′]+f[i][S−S′]}
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