2019ICPC南昌邀请赛A题

当时队友都在做其他的题,我就去看了一下。。。先交几发最小生成树WA下,然后就走了
打完比赛dalao们说是斯坦纳树板板。。。行吧(打完比赛太多了,直到现在才有时间,计蒜客也出复现赛了,就去学了一下

先说一下A题题意:先给n个点,点的名字还是英文的,还要记录一下,然后给出m条边。之后给出4行,每行两个点的名字,表示从点A到点B要有连线。如果4个连线有重复的边,只需算一次。问最后所有对连起来所需要的最短的权值和

然后说一下斯坦纳树吧。。。斯坦纳树主要好像是求解 在图中选择几个点,然后将几个点连接起来的最短的权值和,最小生成树是斯坦纳树的特殊情况emmmmmm

斯坦纳树主要是dp+最短路。在求解最短权值和的时候我们先会用
dp[i][state]=min(dp[i][state1]+dp[i][state2])
在这里插入图片描述i表示以i为根 state表示某个状态(一般用状压表示 1表示连接某个点,0表示不连接某个点)
state1和state2是state两个不同的状态,state1|state2=state
这样是第一步的优化

之后我们要用最短路来优化
dp[i][state]=min(dp[k][state]+map[k][i])
第二步优化,首先i k之间要有边。。。基本和上面差不多

直到斯坦纳树和题意之后就可以套板板了。
但是有一个问题,就是我们在求解的过程中,会发现用斯坦纳树来求的时候树的根只有一个,题目中给的样例如果用这个方式来求解得出来的是25(这样是求出一棵树) 因为题目只需要我们求4对的连线,不需要有多余的边。。所以我们还要枚举一下。。。

枚举也不需要所有点枚举,毕竟n<=30,全部枚举超时了,我们只需要枚举给出的8个点。因为是4对,如果我们选了一个点,那么相对应的另一个点我们也要选取(这是必须的),所以枚举最少一个点,最多4个点(枚举想了好久。。。主要是先选点,然后选状态)
AC代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
using namespace std;
typedef long long int ll;
const int maxm=1010;
const int maxn=35;
const ll INF=1e18;
struct Node
{
	int end,next,value;
}Map[maxm*2];
struct Point
{
	int i,be;		//i  所有点中的位置   be  根中的位置 
}N[maxn];
int head[maxm],sum;
int n,m;
ll dp[maxn][maxm],bin[maxn],ans;
int lin[maxn],Li;
bool vis[maxn];         
map<string,int> num;
queue<int> q;
void addedge(int start,int end,int value)
{
	Map[sum].end=end;
	Map[sum].next=head[start];
	Map[sum].value=value;
	head[start]=sum++;
	Map[sum].end=start;
	Map[sum].next=head[end];
	Map[sum].value=value;
	head[end]=sum++;
}
void Init()
{
	memset(vis,false,sizeof(vis));
	memset(head,-1,sizeof(head));
	for (int i=0;i<bin[8];i++)
		for (int j=1;j<=n;j++)
			dp[j][i]=INF;
	sum=0;return ;
}
void spfa(int s)
{
	while (!q.empty())
		{
			int now=q.front();
			q.pop();vis[now]=false;
		//	cout<<now<<"--------"<<endl;
			for (int i=head[now];i!=-1;i=Map[i].next)
				if (dp[Map[i].end][s]>dp[now][s]+Map[i].value)
					{
				//		cout<<now<<" -> "<<Map[i].end<<endl;
						dp[Map[i].end][s]=dp[now][s]+Map[i].value;
						if (vis[Map[i].end]==false)
							{
								vis[Map[i].end]=true;
								q.push(Map[i].end);
							}
					}
		}
}
ll cho[maxn],book[maxn];
bool v[maxn];
void dfs2(int cnt,int sum,ll pos)	//起点,数量,和 
{
	if (cnt==8)
		{
			ll s=0;
			for (int i=1;i<=sum;i++)
				{
					s+=dp[N[cho[i]].i][book[i]];
			//		cout<<cho[i]<<" "<<book[i]<<" ";
				}
		//	cout<<endl;
			if (ans>s)
				{
					ans=s;
		//			cout<<pos<<endl;
		//			for (int i=1;i<=sum;i++)
		//			 	cout<<N[cho[i]].i<<" "<<book[i]<<" "<<dp[N[cho[i]].i][book[i]]<<endl;
		//			cout<<endl;
				}
			return ;
		}
	for (int i=1;i<=sum;i++)
		if (pos&bin[cnt]||pos&bin[cnt^1]) dfs2(cnt+1,sum,pos);	//不能选择同一对的点
		else 
			{
				book[i]+=bin[cnt]+bin[cnt^1];		//某个状态选了一个点,就要选择同一对点的另一个点
				pos+=bin[cnt]+bin[cnt^1];
				dfs2(cnt+1,sum,pos);
				book[i]-=(bin[cnt]+bin[cnt^1]);
				pos-=(bin[cnt]+bin[cnt^1]);
			}
}
void dfs1(int cnt,int sum,int pos)		//起点,数量   找sum个点 
{
	if (cnt==sum+1)
		{
			ll c=0;
			for (int i=1;i<=sum;i++)
				{
					book[i]=bin[N[cho[i]].be]+bin[N[cho[i]].be^1];
					c+=book[i];
			//		cout<<N[cho[i]].i<<" "<<"="<<book[i]<<" ";
				}
		//	cout<<endl;
			dfs2(0,sum,c);
			return ;
		}
	for (int i=pos;i<8;i++)
		if (cnt==1)
			{
				cho[cnt]=i;
				dfs1(cnt+1,sum,i+1);
			}
		else if (N[i].be!=(N[cho[cnt-1]].be^1))
			{
				cho[cnt]=i;
				dfs1(cnt+1,sum,i+1);
			}
	return ;
}
int main()
{
	cin>>n>>m;
	string s,t;
	int en;
	int w;
	for (int i=1;i<=n;i++)
		{
			cin>>s;
			num[s]=i;
		}
	bin[0]=1;
	for (int i=1;i<=31;i++) bin[i]=bin[i-1]<<1;
	Init();
	for (int i=1;i<=m;i++)
		{
			cin>>s>>t>>w;
			addedge(num[s],num[t],w);
		}
	Li=0;
	for (int i=0;i<4;i++)
		{
			cin>>s>>t;
			dp[num[s]][bin[i*2]]=0;dp[num[t]][bin[i*2+1]]=0;
			N[i*2].be=Li++;N[i*2].i=num[s];
			N[i*2+1].be=Li++;N[i*2+1].i=num[t];
		}
	for (int k=1;k<bin[8];k++)
		{
			for (int i=1;i<=n;i++)
				{
					for (int j=k&(k-1);j;j=(j-1)&k)
						dp[i][k]=min(dp[i][k],dp[i][j]+dp[i][k^j]) ;
					if (dp[i][k]!=INF&&vis[i]==false)
						{
							q.push(i);
							vis[i]=true;
						}
				}
			spfa(k);
		}
	ans=INF;cho[0]=-1;
	for (int i=1;i<=4;i++)
		dfs1(1,i,0);
	cout<<ans<<endl;
	return 0;
}
发布了46 篇原创文章 · 获赞 2 · 访问量 3199

猜你喜欢

转载自blog.csdn.net/z1164754004z/article/details/97394090