【ybtoj】【最小生成树】生物进化

题意

题解

这题不是特别难,但是有思维陷阱

题里不断给出各种祖先差异程度的计算方法(其实就是树上距离的求法),不禁让我想着根据 Da+Db=Dc这种形式来判断祖先关系,然后就没有然后了

实际上,不用管加和判断这些东西,因为每次加入最小生成树的都是直系边(因为直系边最短),而当加完直系边之后由于端点都被走过,所以不会再加入非直系边,就不用考虑那些乱七八糟的祖先关系

由于第一次写 prim 模板,所以细节在代码里标注了一下

关键:跳出常规思维,发现隐含条件

代码

生物进化
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
#define pr pair<int,int>
priority_queue<pr,vector<pr>,greater<pr> >q; 
const int INF = 0x3f3f3f3f,N = 105;
int n,ecnt=-1,head[N*N<<1],dis[N],vis[N],fa[N];
struct edge
{
	int nxt,to,w;
}a[N*N<<1];
void add(int x,int y,int w)
{
	a[++ecnt].nxt=head[x];
	a[ecnt].to=y;
	a[ecnt].w=w;
	head[x]=ecnt;
}
void prim()
{
	memset(dis,0x3f,sizeof(dis));
	dis[1]=0;
	q.push(mp(0,1));
	while(!q.empty())
	{
		int u=q.top().second;q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=head[u];~i;i=a[i].nxt)
		{
			int v=a[i].to;
			if(vis[v]) continue;//注意这里判断不能走回父亲 
			if(dis[v]>a[i].w)
			{
				//printf("(u,v):%d %d\n",u,v);
				fa[v]=u;//这个地方记录直系祖先即可 
				dis[v]=a[i].w;
				q.push(mp(dis[v],v));
			}
		}
	}
}
int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		for(int j=1,x;j<=n;j++)
		{	
			scanf("%d",&x);
			if(i!=j) add(i,j,x),add(j,i,x);//注意建双向边,去除自环 
		}
	prim();
	for(int i=1;i<n;i++)	
		printf("%d\n",fa[i+1]); 
	return 0;
}

 

posted @ 2021-09-07 11:45  conprour  阅读(44)  评论(0编辑  收藏  举报