1. 程式人生 > 實用技巧 >JavaScript 複習07

JavaScript 複習07

對於最小生成樹的定義:在一個有\(n\)個點的圖中,用\(n - 1\)條邊將其連成一棵樹,使得所有\(n - 1\)條邊的權值之和最小。
最小生成樹有兩種做法:Prim和Kruskal,本文章兩個都會介紹。

Prim:
前置知識:
Prim的做法就是把點分為已經加入最小生成樹的和未被加入的,每次把距離已加入的點最近的邊加入最小生成樹。不過記錄邊比較麻煩,我們可以記錄點,記\(v_i\)為節點\(i\)到已加入部分最短的邊的長度,而小根堆記錄\(v_i\)\(i\)

  1. 首先隨便找一個點(一般選1號點)入小根堆。
  2. 每次取出堆頂\(u\)並pop。
  3. 判斷\(u\)是否已經加入最小生成樹
  4. 如果不是,將\(v_u\)
    加入最小生成樹的邊權和
  5. 然後遍歷所有連線的點\(x\)
  6. \(v_x>v_u\),則將\(x\)加入堆。
  7. 重複2,3,4,5,6直到堆為空或者已經加入了\(n-1\)條邊

關於無法形成一個樹:該情況就是在結束後判斷\(n\)個點是否都已經被標記,或者由於記錄了邊的條數,也可以判斷邊數是否為1。

code:

#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,cnt,ans;
int v[5005];
bool f[5005];//f判斷該節點是否已經加入最小生成樹
struct node
{
	int first,second;
	friend bool operator<(node x,node y){return x.first>y.first;}
};
priority_queue<node>q;//小根堆
struct graph
{
	int tot;
	int hd[5005];
	int nxt[400005],to[400005],dt[400005];
	void add(int u,int v,int w)
	{
		tot++;
		nxt[tot]=hd[u];
		hd[u]=tot;
		to[tot]=v;
		dt[tot]=w;
		return ;
	}
}g;//鏈式前向星存圖
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		g.add(u,v,w);
		g.add(v,u,w);
	}
	memset(v,0x3f,sizeof(v));
	q.push((node){0,1});
	v[1]=0;//入堆第一個點
	while(!q.empty()&&cnt<n-1)//cnt記錄邊的條數,若堆不為空或邊數不足n-1則繼續
	{
		int xx=q.top().second;//取出堆頂
		q.pop();//彈出
		if(!f[xx])//若還未加入最小生成樹
		{
			f[xx]=true;//標記為加入
			cnt++;//邊數+1
			ans+=v[xx];//記錄長度
			for(int i=g.hd[xx];i;i=g.nxt[i])//遍歷能到達的點
				if(v[g.to[i]]>g.dt[i])//滿足條件
				{
					v[g.to[i]]=g.dt[i];//先更改v
					q.push((node){v[g.to[i]],g.to[i]});//然後入堆
				}
		}
	}
	for(int i=1;i<=n;i++)
		if(!f[i])
		{
			printf("orz");
			return 0;
		}//判斷是否連通
	printf("%d",ans);
	return 0;
}

Kruskal:
前置知識:並查集
Kruskal的做法:

  1. 把所有邊按順序排序。
  2. 從第一條邊開始列舉。
  3. 如果邊的兩端聯通(用並查集判斷),就跳過。
  4. 否則就加入這條邊,併合並兩個端點的集合。
  5. 重複3,4步直到列舉完。

關於無法形成一個樹:判斷是否所有點都在同一個集合裡。

code:

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,ans;
struct node
{
	int f,t,d;
}a[200005];//存邊,Kruskal不用建圖
bool cmp(node x,node y){return x.d<y.d;}
struct bin
{
	int w[5005];
	int find(int x)
	{
   		if(x==w[x]) return x;
	    w[x]=find(w[x]);
   		return w[x];
	}
	void add(int x,int y)
	{
    	w[find(x)]=find(y);
   		return ;
	}
	bool ask(int x,int y)
	{
   		if(find(x)==find(y)) return true;
    	else return false;
	}
}b;//並查集
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) scanf("%d%d%d",&a[i].f,&a[i].t,&a[i].d);
	for(int i=1;i<=n;i++) b.w[i]=i;//並查集初始化
	sort(a+1,a+m+1,cmp);//按邊權排序
	for(int i=1;i<=m;i++)//列舉每條邊
	{
		if(b.ask(a[i].f,a[i].t)) continue;//連通則跳過
		ans+=a[i].d;//否則記錄
		b.add(a[i].f,a[i].t);//改為連通
	}
	for(int i=2;i<=n;i++)
		if(!b.ask(1,n))
		{
			printf("orz");
			return 0;
		}//判斷是否連通
	printf("%d",ans);
	return 0;
}