UVALive 5717 & HDU 4085 Peach Blossom Spring(DP+斯坦纳树)

5 篇文章 0 订阅

题目链接:点击打开链接

题目算是比较裸的斯坦纳树,求的是最小的斯坦纳森林,而且其中每颗树都要满足【代表家的点的点数要和代表破屋的点的点数一样多】。

首先先求一遍斯坦纳树,这个参考上一篇的斯坦纳树模板。

此处设stein[i][bitmask]为以i为根且包含点集中的bitmask状态的点的情况下的最小斯坦纳树。

其次,设dp[bitmask]为,在包含点集中的bitmask状态的点的情况下的最小满足条件的斯坦纳森林。

则dp[bitmask]有两种转移:

1. stein[i][bitmask],其中i为1到n的所有整数。

这一步其实就是求在bitmask状态下的最小斯坦纳树,将之前求的的最小斯坦纳树做个求最小值就行了。

2. dp[sub1]+dp[sub2],其中sub1和sub2为bitmask的子集,且sub1+sub2=bitmask,而且sub1和sub2要满足【代表家的点的点数要和代表破屋的点的点数一样多】这个条件。

这一步就是指按条件将这个森林劈成两个森林来dp然后进行合并。

注意无解的时候要输出-1。

代码如下:

#include<bits/stdc++.h>
using namespace std;

struct edge
{
	int u,v,w;
	edge(int uu=0,int vv=0,int ww=0):u(uu),v(vv),w(ww){}
};

struct SteinerTree
{
	int n,k;
	int dp[105][10005];
	int st[105];
	bool inque[105][10005];
	queue<int> q;
	vector<edge> e[105];
	
	void init(int nn,int kk,vector<int> c)
	{
		n=nn;k=kk;
		memset(dp,-1,sizeof(dp));
		memset(st,0,sizeof(st));
		for(int i=0;i<kk;i++)
			st[c[i]]=(1<<i);
		for(int i=1;i<=n;i++)
			dp[i][st[i]]=0;
		memset(inque,0,sizeof(inque));
		while(!q.empty())q.pop();
		for(int i=1;i<=n;i++)
			e[i].clear();
	}
	
	void addedge(int u,int v,int w)
	{
		e[u].push_back(edge(u,v,w));
		e[v].push_back(edge(v,u,w));
	}
	
	void SPFA(int b)
	{
		while(!q.empty())
		{
			int u=q.front();
			q.pop();
			inque[u][b]=false;
			for(int i=0;i<e[u].size();i++)
			{
				int &v=e[u][i].v;
				if(dp[v][st[v]|b]==-1||dp[v][st[v]|b]>dp[u][b]+e[u][i].w)
				{
					dp[v][st[v]|b]=dp[u][b]+e[u][i].w;
					if((st[v]|b!=b)||inque[v][b])continue;
					q.push(v);
					inque[v][b]=true;
				}
			}
		}
	}
	
	void BuildTree()
	{
		for(int j=1;j<(1<<k);j++)
		{
			for(int i=1;i<=n;i++)
			{
				if(st[i]&&(st[i]&j)==0)continue;
				for(int sub=(j-1)&j;sub;sub=(sub-1)&j)
				{
					int x=st[i]|sub,y=st[i]|(j-sub);
					if(dp[i][x]!=-1&&dp[i][y]!=-1)
					{
						if(dp[i][j]==-1||dp[i][j]>(dp[i][x]+dp[i][y]))
						{
							dp[i][j]=dp[i][x]+dp[i][y];
						}
					}
				}
				if(dp[i][j]!=-1)
				{
					q.push(i);
					inque[i][j]=true;
				}
			}
			SPFA(j);
		}
	}
}S;

int MinDP(int b)
{
	int ret=S.dp[1][b];
	for(int i=2;i<=S.n;i++)
	{
		if(S.dp[i][b]==-1)continue;
		if(ret==-1||ret>S.dp[i][b])
			ret=S.dp[i][b];
	}
	return ret;
}

int dp[10005];

int dfs(int b)
{
	int k=S.k/2;
	if(dp[b]!=-1)return dp[b];
	dp[b]=MinDP(b);
	for(int sub=(b-1)&b;sub;sub=(sub-1)&b)
	{
		int cnt1=0,cnt2=0,p=sub;
		for(int i=0;i<k;i++)
		{
			if(p&1)cnt1++;
			p>>=1;
		}
		for(int i=0;i<k;i++)
		{
			if(p&1)cnt2++;
			p>>=1;
		}
		if(cnt1!=cnt2)continue;
		if(!(dfs(sub)>=0&&dfs(b-sub)>=0))continue;
		if(dp[b]==-1||dp[b]>dfs(sub)+dfs(b-sub))
			dp[b]=dfs(sub)+dfs(b-sub);
	}
	return dp[b];
}

int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,m,k;
		scanf("%d%d%d",&n,&m,&k);
		vector<int> V;
		for(int i=1;i<=k;i++)
			V.push_back(i);
		for(int i=n-k+1;i<=n;i++)
			V.push_back(i);
		S.init(n,k*2,V);
		for(int i=0;i<m;i++)
		{
			int u,v,w;
			scanf("%d%d%d",&u,&v,&w);
			S.addedge(u,v,w);
		}
		S.BuildTree();
//		for(int i=1;i<=n;i++)
//			for(int j=0;j<(1<<(k*2));j++)
//				printf("dp[%d][%d]=%d\n",i,j,S.dp[i][j]);
		memset(dp,-1,sizeof(dp));
		int ans=dfs((1<<(k*2))-1);
		if(ans!=-1)printf("%d\n",ans);
		else printf("No solution\n");
	}
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值