省政府“暢通工程”的目標是使全省任何兩個村莊間都可以實現公路交通(但不一定有直接的公路相連,只要能間接通過公路可達即可)。經過調查評估,得到的統計表中列出了有可能建設公路的若干條道路的成本。現請你編寫程序,計算出全省暢通需要的最低成本。
輸入:
測試輸入包含若干測試用例。每個測試用例的第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;
}
#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;
}