bzoj4006
斯坦纳树
比之前要求高了一些
其实利用斯坦纳树的dp[i][s]以i为根,S为状态就行了,先跑一遍斯坦纳树,预处理出dp数组,记住每个S的最小值,然后再dp,这里dp必须要求同一种颜色的状态都必须在S里,然后跑枚举子集就行了
#include<bits/stdc++.h> using namespace std; const int N = 1005, inf = 0x3f3f3f3f; struct edge { int nxt, to, w; } e[N * 6]; struct points { int x, c; } a[N]; int n, m, p, cnt = 1; int head[N], sum[11], vis[N], ans[1 << 11], dp[N][1 << 11], tmp[11]; bool check(int S) { memset(tmp, 0, sizeof(tmp)); for(int i = 0; i < p; ++i) if(S >> i & 1) ++tmp[a[i].c]; for(int i = 1; i <= p; ++i) if(tmp[i] && sum[i] != tmp[i]) return false; return true; } void link(int u, int v, int w) { e[++cnt].nxt = head[u]; head[u] = cnt; e[cnt].to = v; e[cnt].w = w; } int main() { scanf("%d%d%d", &n, &m, &p); for(int i = 1; i <= m; ++i) { int u, v, w; scanf("%d%d%d", &u, &v, &w); link(u, v, w); link(v, u, w); } memset(dp, 0x3f3f, sizeof(dp)); for(int i = 0; i < p; ++i) { scanf("%d%d", &a[i].c, &a[i].x); dp[a[i].x][1 << i] = 0; ++sum[a[i].c]; } for(int S = 1; S < (1 << p); ++S) { queue<int> q; for(int i = 1; i <= n; ++i) { for(int S0 = S; S0; S0 = (S0 - 1) & S) dp[i][S] = min(dp[i][S], dp[i][S ^ S0] + dp[i][S0]); if(dp[i][S] < inf) { vis[i] = 1; q.push(i); } } while(!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for(int i = head[u]; i; i = e[i].nxt) if(dp[u][S] + e[i].w < dp[e[i].to][S]) { dp[e[i].to][S] = dp[u][S] + e[i].w; if(!vis[e[i].to]) { vis[e[i].to] = 1; q.push(e[i].to); } } } ans[S] = inf; for(int i = 1; i <= n; ++i) ans[S] = min(ans[S], dp[i][S]); } for(int i = 1; i < (1 << p); ++i) if(check(i)) for(int S = i; S; S = (S - 1) & i) if(check(S)) ans[i] = min(ans[i], ans[i ^ S] + ans[S]); printf("%d\n", ans[(1 << p) - 1]); return 0; }