1. 程式人生 > >【loj】#10066. 「一本通 3.1 練習 1」新的開始 (最小生成樹·Prim)

【loj】#10066. 「一本通 3.1 練習 1」新的開始 (最小生成樹·Prim)

題目描述:

發展採礦業當然首先得有礦井,小 FF 花了上次探險獲得的千分之一的財富請人在島上挖了 nnn 口礦井,但他似乎忘記考慮的礦井供電問題……

為了保證電力的供應,小 FF 想到了兩種辦法:

  1. 在這一口礦井上建立一個發電站,費用為 v(發電站的輸出功率可以供給任意多個礦井)。
  2. 將這口礦井與另外的已經有電力供應的礦井之間建立電網,費用為 p。

小 FF 希望身為「NewBe_One」計劃首席工程師的你幫他想出一個保證所有礦井電力供應的最小花費。

題目連結https://loj.ac/problem/10066

【分析】一開始想複雜了,在想應該不單單是求最小生成樹,因為每個點還有權值,如果是點與邊的組合.... 然而,想太多。只要把d陣列初始賦值為每個點的權值而不是後來通過初始節點更新,就可以了。因為不管怎樣後來還是要對d陣列進行更新,那麼比較點的權值和邊權來決定是加點還是加邊就好。emmm我是這麼理解的,不知道對不對。

然後就是,求最小生成樹了。n的範圍只有300,Prim,然後鄰接矩陣。其他,好像沒啥要注意的了。

【程式碼】

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

const int maxn=300;
const int inf=0x3f3f3f3f;

int vis[maxn];
int e[maxn][maxn];
int d[maxn];
int n,ans;

void read()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++)scanf("%d",&d[i]);
	for(int i=0;i<n;i++)for(int j=0;j<n;j++)
		scanf("%d",&e[i][j]);
}
void Prim(int x)
{
	memset(vis,0,sizeof(vis));
	int minn,k;
	ans=0;
	for(int i=0;i<n;i++)
	{
		minn=inf;
		for(int j=0;j<n;j++)
		{
			if(!vis[j] && minn>d[j])
				minn=d[j],k=j;
		}
		vis[k]=1;ans+=d[k];
		for(int j=0;j<n;j++)
		{
			if(!vis[j] && d[j]>e[j][k])
				d[j]=e[j][k];
		}
	}
}
int main()
{
	ans=0;
	read();
	Prim(0);
	printf("%d\n",ans);
	return 0;
}