csuoj 2171 Steiner Tree

题解:小于n/2的点用steiner tree算法,大于n/2的点用最小生成树。

#include"bits/stdc++.h"
using namespace std;
const int N = 30;
const int inf = 0x3f3f3f3f;
int n,m;
int head[N], sign[N], vis[N];
int f[N][1<<14],st[N],d[1<<14],fa[N];
struct edge{
    int nxt,v,w;
    edge(){}
    edge(int nxt, int v, int w):nxt(nxt),v(v),w(w){}
}E[N*N];
queue<int> q;
struct node{
    int u,v,w;
    node(){}
    node(int u, int v, int w) : u(u), v(v), w(w){}
    bool operator < (const node &a) const{
        return w < a.w;
    }
}p[N*N];

int tail;
inline void init()
{
    tail = 0;
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(st,0,sizeof(st));
}
inline void add(int u, int v, int w)
{
    E[tail] = edge(head[u],v,w); head[u] = tail++;
}

void spfa(int sta)
{
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; ~i; i = E[i].nxt){
            int v = E[i].v, w = E[i].w;
            if(f[v][sta] > f[u][sta] + w){
                f[v][sta] = f[u][sta] + w;
                if(!vis[v]){
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
        vis[u] = 0;
    }
}

//预处理出st数组点的集合和f数组,不同的题目有所不同
void prepare(int S, int k)
{
    for(int i = 1; i <= n; i++)
        for(int j = 0; j < S; j++)
            f[i][j] = inf;
    for(int i = 1; i <= k; i++)
        st[i] = 1<<(i-1), f[i][st[i]] = 0;
}

void stenier(int S, int k)
{
    prepare(S,k);
    for(int sta = 0; sta < S; sta++){
        for(int i = 1; i <= n; i++){
            for(int s = sta; s; s = (s-1)&sta){
                f[i][sta] = min(f[i][sta],f[i][sta^s]+f[i][s]);
            }
            if(f[i][sta] != inf) q.push(i), vis[i] = 1;
        }
        spfa(sta);
    }
}

inline int get_fa(int x)
{
    return fa[x] == x? x : fa[x] = get_fa(fa[x]);
}

int kruskal()
{
    int ans = 0;
    for(int i = 1; i <= n; i++) fa[i] = i;
    for(int i = 1; i <= m; i++) {
        int u = p[i].u, v = p[i].v, w = p[i].w;
        if(sign[u] || sign[v]) continue;
        int fu = get_fa(u), fv = get_fa(v);
        if(fu == fv) continue;
        fa[fu] = fv;
        ans += w;
    }
    for(int i = 1; i <= n; i++) if(sign[i] == 0){
        if(get_fa(i) != get_fa(1)) return inf;
    }
    return ans;
}

void solve2(int nt)
{
    for(int i = 1; i <= n/2; i++)
        sign[i] = 0;
    for(int i = 1; i < (1<<nt); i++) {
        for(int j = 0; j < nt; j++) {
            if(i&(1<<j)) sign[j+1+n/2] = 0;
            else sign[j+1+n/2] = 1;
        }
        d[i] = kruskal();
    }
}

int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif // LOCAL
    while(~scanf("%d%d",&n,&m)){
        init();
        for(int i = 1,u,v,w; i <= m; i++) {
            scanf("%d%d%d",&u,&v,&w);
            p[i] = node(u,v,w);
            add(u,v,w); add(v,u,w);
        }
        sort(p+1,p+1+m);
        int S = 1<<(n/2), k = n/2, nt = n-n/2;
        stenier(S,k);
        solve2(nt);

        int x = 1;
        for(int i = 2; i <= n/2; i++) {
            x = (x<<1) + 1;
            int ans = inf;
            for(int j = 1; j <= n; j++)
                ans = min(ans,f[j][x]);
            printf("%d\n",ans);
        }

        x = 0;
        for(int i = 1; i <= nt; i++) {
            x = (x<<1) + 1;
            int ans = inf;
            for(int j = x; j < (1<<nt); j++)
                if((j&x) == x){
                    ans = min(ans,d[j]);
                }
            printf("%d\n",ans);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_18869763/article/details/83118168