BZOJ 4006: [JLOI2015]管道连接|斯坦纳树

斯坦纳树似乎是带有关键点的最小生成树
暴力状压,然后还有染色,似乎要再套一个状压

#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define T 3333
#define MX 1000000000
using namespace std;
int sc()
{
    int i=0; char c=getchar();
    while(c>'9'||c<'0')c=getchar();
    while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();
    return i;
}
int head[T],nxt[T*3],lst[T*3],v[T*3];
int f[T][T/3],vis[T],st[11];
int n,m,p,P,tot,S;
int g[66];
vector<int> a[11],col[11],b;
queue<int> q;
void insert(int x,int y,int z)
{
    lst[++tot]=y; nxt[tot]=head[x]; v[tot]=z; head[x]=tot;
    lst[++tot]=x; nxt[tot]=head[y]; v[tot]=z; head[y]=tot;
}
void Pretreatment(int p)
{
    for(int i=1;i<=n;i++)
        for(int j=0;j<p;j++)
            f[i][j]=MX;
}
void spfa(int k)
{
    while(!q.empty())
    {
        int x=q.front();q.pop();vis[x]=0;
        for(int i=head[x];i;i=nxt[i])
        {
            if(f[lst[i]][k]>f[x][k]+v[i])
            {
                f[lst[i]][k]=f[x][k]+v[i];
                if(!vis[lst[i]])q.push(lst[i]),vis[lst[i]]=1;
            }
        }
    }
}
int Steiner_tree(int K)
{
    for(int i=1;i<K;i++)
    {
        for(int j=1;j<=n;j++)
        {
            for(int k=i&(i-1);k;k=i&(k-1))
                f[j][i]=min(f[j][i],f[j][k]+f[j][i-k]);
            if(f[j][i]!=MX) q.push(j),vis[j]=1;
        }
        spfa(i);
    }
    int ans=MX;
    for(int i=1;i<=n;i++)
        ans=min(ans,f[i][K-1]);
    return ans;
}
int main()
{
    n=sc(),m=sc(),p=sc();
    for(int i=1;i<=m;i++)
    {
        int x=sc(),y=sc(),z=sc();
        insert(x,y,z);
    }
    for(int i=1;i<=p;i++)
    {
        int x=sc(),y=sc();
        a[x].push_back(y);
    }
    for(int i=1;i<=p;i++)
    {
        if(a[i].size()>1)
        {
            S++;
            for(int j=0;j<a[i].size();j++)
            {
                col[S].push_back(a[i][j]);
            }
        }
    }
    for(int i=1;i<(1<<S);i++) g[i]=MX;
    for(int i=1;i<(1<<S);i++)
    {
        int p=0; b.resize(0);
        for(int j=1;j<=S;j++)
            if(i&(1<<j-1))
            {
                for(int k=0;k<col[j].size();k++)
                {
                    b.push_back(col[j][k]);
                    p++;
                }
            }
        Pretreatment(1<<p);
        for(int j=0;j<b.size();j++) f[b[j]][1<<j]=0;
        g[i]=Steiner_tree(1<<p);
    }
    for(int i=1;i<(1<<S);i++)
    {
        for(int k=i&(i-1);k;k=i&(k-1))
            g[i]=min(g[i],g[k]+g[i-k]);
    }
    cout<<g[(1<<S)-1];
    return 0;   
}

你可能感兴趣的:(斯坦纳树)