bzoj4006 [JLOI2015]管道连接

题目描述

题解:

由于$p<=10$,我们可以暴力枚举覆盖频道状态为$s$的特殊点,每次跑一遍斯坦纳树,

最后合并,方程和斯坦纳树的状态合并方程几乎一样。

代码:

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 1050
#define M 3050
#define ll long long
const ll Inf = 0x3f3f3f3f3f3f3f3fll;
inline int rd()
{
    int f=1,c=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){c=10*c+ch-'0';ch=getchar();}
    return f*c;
}
int n,m,p,q,hed[N],cnt;
struct EG
{
    int to,nxt,w;
}e[2*M];
void ae(int f,int t,int w)
{
    e[++cnt].to = t;
    e[cnt].nxt = hed[f];
    e[cnt].w = w;
    hed[f] = cnt;
}
int ve[12][12],ct[12];
ll tmp[N][1<<10],g[1<<10];
bool vis[N];
queue<int>que;
void spfa(int s)
{
    while(!que.empty())
    {
        int u = que.front();que.pop();
        for(int j=hed[u];j;j=e[j].nxt)
        {
            int to = e[j].to;
            if(tmp[to][s]>tmp[u][s]+e[j].w)
            {
                tmp[to][s]=tmp[u][s]+e[j].w;
                if(!vis[to])vis[to]=1,que.push(to);
            }
        }
        vis[u]=0;
    }
}
void sol(int k)
{
    memset(tmp,0x3f,sizeof(tmp));
    int now = 0;
    for(int i=0;i<q;i++)
        if(k&(1<<i))
            for(int j=0;j<ct[i+1];j++)
                tmp[ve[i+1][j]][1<<now]=0,now++;
    for(int s=1;s<(1<<now);s++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int t=s&(s-1);t;t=s&(t-1))
            {
                tmp[i][s]=min(tmp[i][s],tmp[i][t]+tmp[i][s^t]);
            }
            if(tmp[i][s]!=Inf)que.push(i),vis[i]=1;
        }
        spfa(s);
    }
    for(int i=1;i<=n;i++)
        if(tmp[i][(1<<now)-1]<g[k])
            g[k]=tmp[i][(1<<now)-1];
}
int main()
{
    n = rd(),m = rd(),p = rd();
    for(int f,t,w,i=1;i<=m;i++)
    {
        f=rd(),t=rd(),w=rd();
        ae(f,t,w),ae(t,f,w);
    }
    for(int f,t,i=1;i<=p;i++)
    {
        f=rd(),t=rd();
        ve[f][ct[f]]=t;q+=(!ct[f]);
        ct[f]++;
    }
    memset(g,0x3f,sizeof(g));
    for(int s=1;s<(1<<q);s++)
    {
        sol(s);
        for(int t=s&(s-1);t;t=s&(t-1))
            g[s]=min(g[s],g[t]+g[s^t]);
    }
    printf("%lld\n",g[(1<<q)-1]);
    return 0;
}

 

posted @ 2019-01-02 07:30  LiGuanlin  阅读(121)  评论(0编辑  收藏  举报