1. 程式人生 > >城市建設 藍橋杯

城市建設 藍橋杯

問題描述   棟棟居住在一個繁華的C市中,然而,這個城市的道路大都年久失修。市長準備重新修一些路以方便市民,於是找到了棟棟,希望棟棟能幫助他。

  C市中有n個比較重要的地點,市長希望這些地點重點被考慮。現在可以修一些道路來連線其中的一些地點,每條道路可以連線其中的兩個地點。另外由於C市有一條河從中穿過,也可以在其中的一些地點建設碼頭,所有建了碼頭的地點可以通過河道連線。

  棟棟拿到了允許建設的道路的資訊,包括每條可以建設的道路的花費,以及哪些地點可以建設碼頭和建設碼頭的花費。

  市長希望棟棟給出一個方案,使得任意兩個地點能只通過新修的路或者河道互達,同時花費盡量小。 輸入格式   輸入的第一行包含兩個整數n, m,分別表示C市中重要地點的個數和可以建設的道路條數。所有地點從1到n依次編號。
  接下來m行,每行三個整數a, b, c,表示可以建設一條從地點a到地點b的道路,花費為c。若c為正,表示建設是花錢的,如果c為負,則表示建設了道路後還可以賺錢(比如建設收費道路)。
  接下來一行,包含n個整數w_1, w_2, …, w_n。如果w_i為正數,則表示在地點i建設碼頭的花費,如果w_i為-1,則表示地點i無法建設碼頭。
  輸入保證至少存在一個方法使得任意兩個地點能只通過新修的路或者河道互達。 輸出格式   輸出一行,包含一個整數,表示使得所有地點通過新修道路或者碼頭連線的最小花費。如果滿足條件的情況下還能賺錢,那麼你應該輸出一個負數。 樣例輸入 5 5
1 2 4
1 3 -1
2 3 3
2 4 5
4 5 10
-1 10 10 1 1 樣例輸出 9 樣例說明   建設第2、3、4條道路,在地點4、5建設碼頭,總的花費為9。 資料規模和約定   對於20%的資料,1<=n<=10,1<=m<=20,0<=c<=20,w_i<=20;
  對於50%的資料,1<=n<=100,1<=m<=1000,-50<=c<=50,w_i<=50;
  對於70%的資料,1<=n<=1000;

  對於100%的資料,1 <= n <= 10000,1 <= m <= 100000,-1000<=c<=1000,-1<=w_i<=1000,w_i≠0。

解題思路:

這道題目坑點有點多;

首先是對題目的理解:如果有某倆個結點不通,我們可以通過在這倆個地方架設碼頭,只要有碼頭的地方都是相通的;

1:我們可以通過kruskal演算法求出花費最少的路徑

2:怎麼處理架設碼頭的點呢?我們可以另外架設一個結點0,將所有假設碼頭的點和0結點相連然後在處理;

3:因為架設碼頭會多出一個點,所以我們要分開判斷,以防止結點數的判斷上出問題

{

1>:如果不架設碼頭就可以聯通,我們取架設碼頭和不架設碼頭的最小值;

2>:如果不架設碼頭不連通,則直接在架設碼頭的情況下求解;

有的人也許會疑問,為什麼不直接放到一起求呢?

因為架設碼頭的話會多出一個結點,因此如果直接求,我們的kruskal就會多出一個路徑用來包含多出的結點,此時如果直接放到一起求,就有可能使結果變大;所以要分開求;

}

4:最坑的是有的路徑可以盈利,為了使花費最小,這些路徑不管怎麼樣都要加上,不能像正常的kruskal演算法因為已經有了更小的連線就不加了;

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 200010

struct Edge{
	int from,to,w;
	Edge(){}
	Edge(int a,int b,int c):from(a),to(b),w(c){}
	bool operator <(const Edge& t)const{
	return w<t.w;}
}edge[maxn];

int father[maxn];
int n,m;

void init()
{
	for(int i=0;i<=n;i++)
	father[i]=i;
}

int find(int x)
{
	int temp;
	temp=x;
	while(x!=father[x])
	{
		x=father[x];
	}
	int root=x;
	x=temp;
	while(x!=father[x])
	{
		temp=father[x];
		father[x]=root;
		x=temp;
	}
	return root;
}

void merge(int a,int b)
{
	int x,y;
	x=find(a);
	y=find(b);
	if(x<y)
	father[y]=x;
	else
	father[x]=y;
}
int kruskal(int nn,int mm)
{
	int sum=0;
	int num=0;
	init();
	sort(edge,edge+mm);
	for(int i=0;i<mm;i++)
	{
		int from=find(edge[i].from);
		int to=find(edge[i].to);
		if(from!=to)
		{
			merge(from,to);
			sum+=edge[i].w;
			num++;
		}
		else if(edge[i].w<0)
		sum+=edge[i].w;
	}
	if(num!=nn-1)
	return -1;
	else
	return sum;
}
int main() 
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++)
	{
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		edge[i]=Edge(a,b,c);
	}
	int cnt=0;
	for(int i=1;i<=n;i++)
	{
		int a;
		scanf("%d",&a);
		if(a!=-1)
		{
			edge[m+cnt]=Edge(0,i,a);
			cnt++;
		}
	}
	int ans=kruskal(n,m);
	if(ans!=-1)
	{
		ans=min(ans,kruskal(n+1,m+cnt));
	}
	else
	ans=kruskal(n+1,m+cnt);
	printf("%d\n",ans);
}