1024.暢通工程

題目描述:
省政府“暢通工程”的目標是使全省任何兩個村莊間都可以實現公路交通(但不一定有直接的公路相連,只要能間接通過公路可達即可)。經過調查評估,得到的統計表中列出了有可能建設公路的若干條道路的成本。現請你編寫程序,計算出全省暢通需要的最低成本。
輸入:
測試輸入包含若干測試用例。每個測試用例的第1行給出評估的道路條數 N、村莊數目M (N, M < =100 );隨後的 N 行對應村莊間道路的成本,每行給出一對正整數,分別是兩個村莊的編號,以及此兩村莊間道路的成本(也是正整數)。爲簡單起見,村莊從1到M編號。當N爲0時,全部輸入結束,相應的結果不要輸出。
輸出:
對每個測試用例,在1行裏輸出全省暢通需要的最低成本。若統計數據不足以保證暢通,則輸出“?”。
樣例輸入:
3 3
1 2 1
1 3 2
2 3 4
1 3
2 3 2
0 100
樣例輸出:
3

?


兩個思路:

1.DFS + Prim

2.並查集 + Prim


1.DFS + Prim

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int MaxInt = 9999;
const int MaxNum = 1000;

int c[MaxNum][MaxNum];  //鄰接矩陣
int dist[MaxNum];       //每個節點到源點的最短距離
bool visited[MaxNum];   //每個節點是否訪問過

void Prim(int s,int n)
{
	for(int i = 1; i <= n; ++i)
		dist[i] = c[s][i];

	dist[s] = 0;
	visited[s] = true;

	//依次訪問接下來的n - 1個unknown節點
	for(int i = 1; i <= n - 1; ++i)
	{
		int tmp = MaxInt; //當前最短距離
		int v = s; // 當前的節點

		//在所有unknown節點中找到和源點距離最短的節點
		for(int j = 1; j <= n; ++j)
		{
			if(!visited[j] && dist[j] < tmp)
			{
				v = j;
				tmp = dist[j];
			}
		}

		//找到了和源點距離最短的unknown節點,更改其訪問數組
		visited[v] = true;

		//然後更新其鄰接的邊,所有的邊都要訪問一次,都可能會更新
		for(int j = 1; j <= n; ++j)
		{
			if(!visited[j] && c[v][j] < MaxInt)
			{
				dist[j] = min(dist[j],c[v][j]);  //prim算法!!
			}
		}
	}
}

void DFS(int i, int n, int &count)
{
    visited[i] = true;
    count++;
    for(int j = 1; j <= n; ++j)
    {
        if(!visited[j] && c[i][j] < MaxInt)
        {
            DFS(j,n,count);
        }
    }
}

int main()
{
	int N,M;  //N爲道路條數,M爲村莊數目

	while(cin >> N >> M)
	{
		//跳出條件
		if(N == 0)
			break;
		//初始化鄰接矩陣
		for(int i = 1; i <= M; ++i)
			for(int j = 1; j <= M; ++j)
				c[i][j] = MaxInt;

		for(int i = 0; i < N; ++i)
		{
			int p,q,len;
			cin >> p >> q >> len;
			if(len < c[p][q])//如果有重邊
			{
				c[p][q] = len;
				c[q][p] = len;  //無向圖 如果有向圖這句話就去掉
			}
		}

		//初始化最短距離
		for(int i = 1; i <= M; ++i)
			dist[i] = MaxInt;

		//初始化訪問數組
		for(int i = 1; i <= M; ++i)
			visited[i] = false;

        int count = 0;
        DFS(1,M,count);
        if(count != M)
        {
            cout << "?" << endl;
        }
        else
        {
            memset(visited,0,sizeof(visited));

            Prim(1,M);
            int min_dist = 0;

            for(int i = 1; i <= M; ++i)
                min_dist += dist[i];

            cout << min_dist << endl;
        }
	}
	return 0;
}


2.並查集 + Prim

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>

using namespace std;

const int MaxInt = 9999;
const int MaxNum = 1000;

int c[MaxNum][MaxNum];  //鄰接矩陣
int dist[MaxNum];       //每個節點到源點的最短距離
bool visited[MaxNum];   //每個節點是否訪問過

void Prim(int s,int n)
{
	for(int i = 1; i <= n; ++i)
		dist[i] = c[s][i];

	dist[s] = 0;
	visited[s] = true;

	//依次訪問接下來的n - 1個unknown節點
	for(int i = 1; i <= n - 1; ++i)
	{
		int tmp = MaxInt; //當前最短距離
		int v = s; // 當前的節點

		//在所有unknown節點中找到和源點距離最短的節點
		for(int j = 1; j <= n; ++j)
		{
			if(!visited[j] && dist[j] < tmp)
			{
				v = j;
				tmp = dist[j];
			}
		}

		//找到了和源點距離最短的unknown節點,更改其訪問數組
		visited[v] = true;

		//然後更新其鄰接的邊,所有的邊都要訪問一次,都可能會更新
		for(int j = 1; j <= n; ++j)
		{
			if(!visited[j] && c[v][j] < MaxInt)
			{
				dist[j] = min(dist[j],c[v][j]);  //prim算法!!
			}
		}
	}
}

int find(vector<int> &a, int x)
{
	if(a[x] < 0)
		return x;
	else
		return a[x] = find(a,a[x]);
}

void unionSets(vector<int> &a, int root1,int root2)
{
	int b = find(a,root1);
	int c = find(a,root2);
	if(b == c)
		return;
	if(a[b] < a[c])
	{
		a[b] += a[c];
		a[c] = b;
	}
	else
	{
		a[c] += a[b];
		a[b] = c;
	}
}


int main()
{
	int N,M;  //N爲道路條數,M爲村莊數目
    vector<int> ivec; //並查集

	while(cin >> N >> M)
	{
		//跳出條件
		if(N == 0)
			break;
        ivec.assign(M+1,-1); //並查集初始化
		//初始化鄰接矩陣
		for(int i = 1; i <= M; ++i)
			for(int j = 1; j <= M; ++j)
				c[i][j] = MaxInt;

		for(int i = 0; i < N; ++i)
		{
			int p,q,len;
			cin >> p >> q >> len;
			unionSets(ivec,p,q); //插入並查集
			if(len < c[p][q])//如果有重邊
			{
				c[p][q] = len;
				c[q][p] = len;  //無向圖 如果有向圖這句話就去掉
			}
		}

		//初始化最短距離
		for(int i = 1; i <= M; ++i)
			dist[i] = MaxInt;

		//初始化訪問數組
		for(int i = 1; i <= M; ++i)
			visited[i] = false;

        int count = 0;  //並查集的集合數
        for(int i = 1; i <= M; ++i)  
        {
            if(find(ivec,i) == i)  
                count++;
        }
        if(count != 1)  //如果並查集的集合數不爲1個,證明不聯通
        {
            cout << "?" << endl;
        }
        else
        {
            memset(visited,0,sizeof(visited));

            Prim(1,M);
            int min_dist = 0;

            for(int i = 1; i <= M; ++i)
                min_dist += dist[i];

            cout << min_dist << endl;
        }

        ivec.clear();
	}
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章