• 【BZOJ4006】【JLOI2015】管道连接


    Description

      
      传送门
      
      
      
      
      

    Solution

      
      题目要求相同颜色的点必须在一个连通块中,但会有多个颜色同属一个连通块使得解更优的情况。
      
      想一想DP能否行得通:设(g_i)表示已考虑颜色状态为(i)时,最小合法方案的代价。
      
      首先,(g_i)可以有一个直观的初值:由颜色属于(i)的点构建的一棵最小生成树的边权和。(初始化)
      
    ​  接下来,如何考虑两部分颜色各自的连通块合起来作为最优解的情况?(两子集合并更新)
      
    ​  更新(g_i)时,我们枚举(i)的两个不相交子集(s_1,s_2)(s_1|s_2=i),并用(g_{s_1}+g_{s_2})来更新(g_i),即直接尝试将两个连通块取交。不需要担心两部分会有重复计算某一条边的情况,因为这是子集枚举DP,有边重复计算的转移自然不会成为最优转移,最优的转移一定会被枚举到(或者就是初值最优)。
      
    ​  以上两种方法并用,就可以顺利DP出(g)
      
    ​  答案就是(g_{all})(all)是包含所有颜色的集合。
      
    ​  对于(g)的初值,直接用斯坦纳树计算所有关键点的不同组合的最小生成树即可。
      
      
      

    Code

      

    #include <cstdio>
    #include <algorithm>
    #include <queue>
    using namespace std;
    const int N=1005,M=3005,C=10,INF=1e9;
    int n,m,p,col[N],tin[N][2],colst[C+1];
    int idcnt,id[N],rep[C+1];
    int clis[C+1],ccnt;
    int f[N][1<<C],g[1<<C];
    queue<int> q;
    bool inq[N];
    int h[N],tot;
    struct Edge{int v,w,next;}e[M*2];
    inline int bit(int i){return !i?0:(1<<(i-1));}
    inline bool in(int st,int i){return (st>>(i-1))&1;}
    inline void addEdge(int u,int v,int w){
    	e[++tot]=(Edge){v,w,h[u]}; h[u]=tot;
    	e[++tot]=(Edge){u,w,h[v]}; h[v]=tot;
    }
    void read(){
    	scanf("%d%d%d",&n,&m,&p);
    	for(int i=1,u,v,w;i<=m;i++){
    		scanf("%d%d%d",&u,&v,&w);
    		addEdge(u,v,w);
    	}
    	for(int i=1,c,u;i<=p;i++){
    		scanf("%d%d",&c,&u);// c ?
    		tin[i][0]=c; tin[i][1]=u;
    		clis[++ccnt]=c;
    	}
    	sort(clis+1,clis+1+ccnt);
    	ccnt=unique(clis+1,clis+1+ccnt)-clis-1;
    	for(int i=1,u,c;i<=p;i++){
    		c=lower_bound(clis+1,clis+1+ccnt,tin[i][0])-clis;
    		u=tin[i][1];
    		col[u]=c;
    		id[u]=++idcnt;			
    		rep[c]=u;
    		colst[c]|=bit(id[u]);
    	}
    }
    void spfa(int st){
    	while(!q.empty()){
    		int u=q.front(); q.pop();
    		inq[u]=false;
    		for(int i=h[u],v;i;i=e[i].next){
    			v=e[i].v;
    			int ns=st|bit(id[v]);
    			if(f[u][st]+e[i].w<f[v][ns]){
    				f[v][ns]=f[u][st]+e[i].w;
    				if(!inq[v]){
    					inq[v]=true;
    					q.push(v);
    				}
    			}
    		}
    	}
    }
    void steinerTree(){
    	int all=1<<p;
    	for(int i=1;i<=n;i++){
    		for(int j=0;j<all;j++) f[i][j]=INF;
    		f[i][bit(id[i])]=0;
    	}
    	for(int j=1;j<all;j++){
    		for(int i=1;i<=n;i++)
    			if(!(id[i]&&!in(j,id[i]))){
    				for(int sub=(j-1)&j;sub;sub=(sub-1)&j){
    					int x=sub|bit(id[i]),y=(j^sub)|bit(id[i]);
    					if(f[i][x]+f[i][y]<f[i][j])
    						f[i][j]=f[i][x]+f[i][y];
    				}
    				if(f[i][j]!=INF)
    					q.push(i),inq[i]=true;
    		}
    		spfa(j);
    	}
    }
    void solve(){
    	int all=1<<ccnt;
    	for(int i=1;i<all;i++){
    		int st=0,rt=0;
    		for(int j=1;j<=ccnt;j++)
    			if(in(i,j))
    				st|=colst[j],rt=rep[j];
    		g[i]=f[rt][st];
    		for(int sub=(i-1)&i;sub;sub=(sub-1)&i)
    			g[i]=min(g[i],g[sub]+g[i^sub]);
    	}
    	printf("%d
    ",g[all-1]);
    }
    int main(){
    	read();
    	steinerTree();
    	solve();
    	return 0;
    }
    
  • 相关阅读:
    python异常
    linux下进行base64编码解码
    mybatis参数映射
    Mybatis--映射器注解
    Mybatis--映射器注解
    Mybatis--Statement Builders
    Mybatis--Statement Builders
    在MySql中如何定义像Java中类型的Boolean类型
    在MySql中如何定义像Java中类型的Boolean类型
    Navicat导入导出数据表
  • 原文地址:https://www.cnblogs.com/RogerDTZ/p/9248671.html
Copyright © 2020-2023  润新知