1. 程式人生 > 其它 >【ybtoj】【最小生成樹】生物進化

【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;
}