http://acm.hdu.edu.cn/showproblem.php?pid=4085
新学习了斯坦纳树,记录一下。
我是从http://endlesscount.blog.163.com/blog/static/821197872012525113427573/学习的斯坦纳树
斯坦纳树用于解决保证指定节点连接的情况下,求边权总和最小的问题。算是用最短路辅助的DP问题
斯坦纳树核心部分:
for(int state = 0; state < endState; state++){
for(int root = 1; root <= n; root++){
for (int sub = (state-1)&state; sub; sub = (sub-1)&state){
mincost[root][state] = min(mincost[root][state],
mincost[root][sub|st[root]] + mincost[root][(state-sub)|st[root]]);
}
if(mincost[root][state] < INF){
isVis[root][state] = true;
que.push(root*base+state);
}
}
spfa();
}
mincost[root][state] 表示以root为根,连接状态为state构造出来的图的边权总和, sub 是 state 的子状态
spfa部分:
void spfa(){
while(!que.empty()){
int now = que.front();
que.pop();
int root = now / base;
int state = now % base;
isVis[root][state] = false;
for (int k = head[root]; k != -1; k = edges[k].next){
int to = edges[k].to, weight = edges[k].length;
if (update(to, state|st[to], mincost[root][state] + weight)
&& state|st[to] == state
&& !isVis[to][state])
isVis[to][state] = true;
que.push(to*base + state);
}
}
}
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int INF = 1e6+10;
int n, m, K, endSt, tot;
int mincost[55][1<<11], st[55];
int dp[1<<11];
int head[55];
bool vis[55][1<<11];
queue<int> que;
struct Edge{
int to, length;
int next;
Edge(){}
Edge(int t, int len){
to = t;
length = len;
next = -1;
}
}edges[2005];
void addEdge(int from, int to, int len){
edges[++tot] = Edge(to, len);
edges[tot].next = head[from];
head[from] = tot;
}
void input(){
tot = 0;
memset(head, -1, sizeof(head));
memset(vis, false, sizeof(vis));
memset(st, 0, sizeof(st));
scanf("%d%d%d", &n, &m, &K);
for (int i = 0; i < m; i++){
int from, to, len;
scanf("%d%d%d", &from, &to, &len);
addEdge(from, to, len);
addEdge(to, from, len);
}
endSt = 1<<(2*K);
for (int i = 1; i <= n; i++){
for (int j = 0; j < endSt; j++){
mincost[i][j] = INF;
}
}
for (int i = 1; i <= K; i++){
st[i] = 1<<(i-1);
mincost[i][st[i]] = 0;
st[n-i+1] = 1<<(K+i-1);
mincost[n-i+1][st[n-i+1]] = 0;
}
while(!que.empty())
que.pop();
}
bool check(int state){
int res = 0;
for (int i = 0; state; i++, state>>=1){
res += (state&1)*(i < K ? 1 : -1);
}
return res == 0;
}
bool update(int root, int state, int weight){
if (weight < mincost[root][state]){
mincost[root][state] = weight;
return true;
}
return false;
}
void spfa(){
while(!que.empty()){
int now = que.front();
que.pop();
int root = now / 10000;
int state = now % 10000;
vis[root][state] = false;
for (int k = head[root]; k != -1; k = edges[k].next){
int to = edges[k].to, weight = edges[k].length;
if (update(to, state|st[to], mincost[root][state]+weight)
&& state == (state|st[to])
&& !vis[to][state]){
vis[to][state] = true;
que.push(to*10000+state);
}
}
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
input();
for (int state = 0; state < endSt; state++){
for (int root = 1; root <= n; root++){
for (int sub = (state-1)&state; sub; sub = (sub-1)&state){
mincost[root][state] = min(mincost[root][state],
mincost[root][sub|st[root]] + mincost[root][(state-sub)|st[root]]);
}
if (mincost[root][state] < INF){
que.push(root*10000+state);
vis[root][state] = true;
}
}
spfa();
}
for (int state = 0; state < endSt; state++){
dp[state] = INF;
for (int root = 1; root <= n; root++){
dp[state] = min(mincost[root][state], dp[state]);
}
}
for (int state = 1; state < endSt; state++){
if (!check(state))
continue;
for (int sub = (state-1)&state; sub; sub = (sub-1)&state){
if (!check(sub))
continue;
dp[state] = min(dp[state], dp[sub] + dp[state-sub]);
}
}
if (dp[endSt-1] >= INF)
printf("No solution\n");
else
printf("%d\n", dp[endSt-1]);
}
return 0;
}