zoukankan      html  css  js  c++  java
  • 【BZOJ4006】【JLOI2015】管道连接(斯坦纳树)

    看到题面容易想到跟斯坦纳树有关。

    那么我们不妨设 f ( i , s t a ) f(i,sta) f(i,sta) 表示根为 i i i,关键点的状压状态为 s t a sta sta 时的最小代价。

    那么所有的 f ( i , s t a ) f(i,sta) f(i,sta) 我们都可以用斯坦纳树的模板求出来。

    现在考虑如何达到题目的要求。

    因为考虑到频道的数量也小于 10 10 10,所以考虑一下能不能也用状压解决。

    g ( s t a ′ ) g(sta') g(sta) 表示频道的状压状态为 s t a ′ sta' sta 时的最小代价。(如果 s t a ′ sta' sta 二进制下的第 k k k 位是 1 1 1 就说明第 k k k 个频道的关键点都已经联通了,否则不连通)。

    然后设 s t a p ( i ) stap(i) stap(i) 表示第 i i i 个频道的所有关键点的状压状态(类似于 f ( i , s t a ) f(i,sta) f(i,sta) 中的 s t a sta sta)。

    显然,这个东西可以在读入的时候预处理。

    然后说一下我一开始的想法:

    对于每一个频道 i i i,把 g ( 1 < < i ) g(1<<i) g(1<<i) 的值设为 min ⁡ ( f ( j , s t a p i ) ) min(f(j,stap_i)) min(f(j,stapi))。然后其他的 g ( ) g() g() 都设为 inf ⁡ inf inf

    然后枚举每一个 s t a ′ sta' sta 的状态,更新: g ( s t a ′ ) = min ⁡ ( g ( s ) + g ( s t a ′ − s ) ) g(sta')=min(g(s)+g(sta'-s)) g(sta)=min(g(s)+g(stas))。(其中 s s s s t a ′ sta' sta 的子集)

    但是我发现了一个重要的问题:

    比如说这个图中,所有节点都是关键节点,点 1 1 1 4 4 4 是频道 1 1 1 的关键节点,点 2 2 2 3 3 3 是频道 2 2 2 的关键节点。(点上的黑色数字代表点的编号,红色数字代表这个点所属的频道, 边上的数字代表这条边的权值)。

    显然有 g ( ( 1 ) 2 ) = min ⁡ f ( i , ( 1001 ) 2 ) = w 1 + w 2 + w 3 g((1)_2)=min f(i,(1001)_2)=w_1+w_2+w_3 g((1)2)=minf(i,(1001)2)=w1+w2+w3 g ( ( 10 ) 2 ) = min ⁡ f ( i , ( 0110 ) 2 ) = w 2 g((10)_2)=min f(i,(0110)_2)=w_2 g((10)2)=minf(i,(0110)2)=w2

    那么会得出来: g ( ( 11 ) 2 ) = w 1 + 2 w 2 + w 3 g((11)_2)=w_1+2w_2+w_3 g((11)2)=w1+2w2+w3

    但显然 g ( ( 11 ) 2 ) g((11)_2) g((11)2) 应该等于 w 1 + w 2 + w 3 w_1+w_2+w_3 w1+w2+w3

    这时候就会发现这样 dp 会算重边。

    如何解决?

    我想到的解决办法:

    我们一开始初始化 g ( ) g() g() 的时候,本来是只初始化所有的 g ( 1 < < i ) g(1<<i) g(1<<i),而我们现在把 g ( ) g() g() 的每一个状态都初始化。

    意思是说,对于每一个 s t a ′ sta' sta,我们都用 f ( ) f() f() g ( s t a ′ ) g(sta') g(sta) 进行初始化。

    具体过程可以看代码,用 dfs 实现。

    最后再按原来的方法,更新: g ( s t a ′ ) = min ⁡ ( g ( s ) + g ( s t a ′ − s ) ) g(sta')=min(g(s)+g(sta'-s)) g(sta)=min(g(s)+g(stas))。(其中 s s s s t a ′ sta' sta 的子集)

    代码如下:

    #include<bits/stdc++.h>
    
    #define N 1010
    #define M 3010
    
    using namespace std;
    
    int n,m,p,anssta,id[15],num[15],stap[15];
    int cnt,head[N],w[M<<1],to[M<<1],nxt[M<<1];
    int f[N][1025],g[1025];
    bool inq[N];
    
    //anssta是答案的状压状态(形如sta')
    //stap是每一个频道对应的状压状态(形如sta)
    
    queue<int>q;
    
    void adde(int u,int v,int wi)
    {
    	to[++cnt]=v;
    	w[cnt]=wi;
    	nxt[cnt]=head[u];
    	head[u]=cnt;
    }
    
    void spfa(int sta)
    {
    	while(!q.empty())
    	{
    		int u=q.front();
    		q.pop();
    		inq[u]=false;
    		for(int i=head[u];i;i=nxt[i])
    		{
    			int v=to[i];
    			if(f[u][sta]+w[i]<f[v][sta])
    			{
    				f[v][sta]=f[u][sta]+w[i];
    				if(!inq[v])
    				{
    					inq[v]=true;
    					q.push(v);
    				}
    			}
    		}
    	}
    }
    
    void dfs(int k,int sum,int sump)
    {
    	if(k==p+1)
    	{
    		for(int i=1;i<=n;i++)
    			g[sump]=min(g[sump],f[i][sum]);
    		return;
    	}
    	dfs(k+1,sum|stap[k],sump|(1<<(k-1)));//枚举选这个频道(sta'的第k位是1)
    	dfs(k+1,sum,sump);//不选这个频道(sta'的第k位是0)
    }
    
    int main()
    {
    	memset(f,0x3f,sizeof(f));
    	memset(g,0x3f,sizeof(g));
    	scanf("%d%d%d",&n,&m,&p);
    	for(int i=1;i<=m;i++)
    	{
    		int u,v,w;
    		scanf("%d%d%d",&u,&v,&w);
    		adde(u,v,w),adde(v,u,w);
    	}
    	for(int i=1;i<=p;i++)
    	{
    		scanf("%d%d",&id[i],&num[i]);
    		f[num[i]][1<<(i-1)]=0;
    		stap[id[i]]|=(1<<(i-1));
    		anssta|=(1<<(id[i]-1));
    	}
    	int maxn=(1<<p)-1;
    	for(int sta=1;sta<=maxn;sta++)
    	{
    		for(int i=1;i<=n;i++)
    		{
    			for(int now=sta;now;now=sta&(now-1))
    				f[i][sta]=min(f[i][sta],f[i][now]+f[i][sta-now]);
    			if(f[i][sta]!=0x3f3f3f3f)
    			{
    				q.push(i);
    				inq[i]=true;
    			}
    		}
    		spfa(sta);
    	}
       //以上是模板斯坦纳树
    	dfs(1,0,0);
    	for(int sta=1;sta<=anssta;sta++)
    		for(int now=sta;now;now=sta&(now-1))
    			g[sta]=min(g[sta],g[now]+g[sta-now]);
    	printf("%d
    ",g[anssta]);
    	return 0;
    }
    
  • 相关阅读:
    春季学期第十二周作业
    2019春第三次课程设计实验报告
    春季学期第十一周作业
    春季学期第十周作业
    春季学期第九周作业
    第五周总结 & 实验报告(三)
    第四周总结 & 实验报告(二)
    实验报告(一)&第三周总结
    第二周小结
    2019春总结
  • 原文地址:https://www.cnblogs.com/ez-lcw/p/14448672.html
Copyright © 2011-2022 走看看