• 【BZOJ4774】修路(动态规划,斯坦纳树)


    【BZOJ4774】修路(动态规划,斯坦纳树)

    题面

    BZOJ

    题解

    先讲怎么求解最小斯坦纳树。
    先明白什么是斯坦纳树。
    斯坦纳树可以认为是最小生成树的一般情况。最小生成树是把所有给定点都要加入到联通块中。而斯坦纳树不一样,斯坦纳树只需要把指定点集中的所有点全部加入到联通块中,并且允许使用点集以外的点。
    然而求解最小斯坦纳树是一个(NP)问题,所以只能状压解决。
    (f[S][i])表示指定点的联通情况为(S),并且当且的斯坦纳树以(i)为根,(i)可以是图上任意一个点。
    考虑如何转移:
    (f[S][i] ightarrow f[T][i]+f[Soplus T][i],S&T=0)
    这个转移的含义是,你以当前点为根的斯坦纳树,可以拆分为两个以当前点为根的斯坦纳树。
    另外一个转移是换根:
    (f[S][i] ightarrow f[S][j]+e[i][j]),其中(e[i][j])是链接(i,j)的边的边长。
    这一步你可以认为一开始没有联通(j),现在我们换根,所以要把它给添加进来。
    但是发现第二个转移具有后效性,所以写成(SPFA)的形式。

    接着考虑怎么求解本题的问题,也就是最小斯坦纳森林。
    (g[S])表示联通了点集(S)的最小斯坦纳森林。
    那么,如果(S)中要求在一个联通块的点全部都连在了一起,那么显然它可以和一个无交集,并且同时满足要求连接在一起的点都连在一起的话,这两个集合显然可以取并集转移。
    (g[S]=g[T]+g[Soplus T]),条件是(T)(Soplus T)这两个集合中如果包含了某个点,就必定包含要求连接在一起的点。
    这样子就做完了。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    #include<queue>
    using namespace std;
    #define ll long long
    #define MAX 10100
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int n,m,D;
    struct Line{int v,next,w;}e[MAX<<1];
    int h[MAX],cnt=1;
    inline void Add(int u,int v,int w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
    int f[1<<8][MAX],g[1<<8];
    bool vis[MAX];
    queue<int> Q;
    void SPFA(int *f)
    {
    	while(!Q.empty())
    	{
    		int u=Q.front();Q.pop();
    		for(int i=h[u];i;i=e[i].next)
    			if(f[e[i].v]>f[u]+e[i].w)
    			{
    				f[e[i].v]=f[u]+e[i].w;
    				if(!vis[e[i].v])Q.push(e[i].v),vis[e[i].v]=true;
    			}
    		vis[u]=false;
    	}
    }
    bool check(int s){return (s&((1<<D)-1))==(s>>D);}
    int main()
    {
    	n=read();m=read();D=read();
    	for(int i=1;i<=m;++i)
    	{
    		int u=read(),v=read(),w=read();
    		Add(u,v,w);Add(v,u,w);
    	}
    	memset(f,63,sizeof(f));memset(g,63,sizeof(g));
    	for(int i=1;i<=D;++i)f[1<<(i-1)][i]=f[1<<(D+i-1)][n-i+1]=0;
    	int S=1<<(D<<1);
    	for(int i=0;i<S;++i)
    	{
    		for(int j=1;j<=n;++j)
    		{
    			for(int k=i&(i-1);k;k=(k-1)&i)
    				f[i][j]=min(f[i][j],f[k][j]+f[i^k][j]);
    			if(f[i][j]<=1e9)Q.push(j),vis[j]=true;
    		}
    		SPFA(f[i]);
    		for(int j=1;j<=n;++j)g[i]=min(g[i],f[i][j]);
    	}
    	for(int i=0;i<S;++i)
    		for(int t=(i-1)&i;t;t=(t-1)&i)
    			if(check(t)&&check(i^t))
    				g[i]=min(g[i],g[t]+g[i^t]);
    	printf("%d
    ",g[S-1]<=1e9?g[S-1]:-1);
    	return 0;
    }
    
    
  • 相关阅读:
    Java面试题(01)
    HTML学习笔记16——尺寸的表示_px、%、em三种
    HTML学习笔记14——HTML 有语义的标签3_字符实体
    HTML13_有语义的标签2_a标签(超链接,锚点,伪类)
    HTML学习笔记12——HTML 有语义的标签1(h,p标签,img标签,列表,表格_制作课程表)
    HTML学习笔记11——CSS 初始化
    HTML学习笔记9——CSS精确控制背景图片位置——background-position
    HTML学习笔记8——CSS设置背景图片
    CSS控制文字的一些基本属性的使用
    HTML学习笔记5——盒模型(这里有棵圣诞树。)
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9670025.html
Copyright © 2020-2023  润新知