貪心策略練習與總結
阿新 • • 發佈:2019-01-25
練習三,還是暢通工程
- 題目描述:
- 某省調查鄉村交通狀況,得到的統計表中列出了任意兩村莊間的距離。省政府“暢通工程”的目標是使全省任何兩個村莊間都可以實現公路交通(但不一定有直接的公路相連,只要能間接通過公路可達即可),並要求鋪設的公路總長度為最小。請計算最小的公路總長度。
- 輸入:
-
測試輸入包含若干測試用例。每個測試用例的第1行給出村莊數目N ( < 100 );隨後的N(N-1)/2行對應村莊間的距離,每行給出一對正整數,分別是兩個村莊的編號,以及此兩村莊間的距離。為簡單起見,村莊從1到N編號。
當N為0時,輸入結束,該用例不被處理。
- 輸出:
-
對每個測試用例,在1行裡輸出最小的公路總長度。
- 樣例輸入:
-
3 1 2 1 1 3 2 2 3 4 4 1 2 1 1 3 4 1 4 1 2 3 3 2 4 2 3 4 5 0
- 樣例輸出:
-
3 5
#include "vector" #include "string" #include "algorithm" #include <iostream> #include "stack" #include <cmath> #include <set> using namespace std; class Edge { public: int acity;//城市a int bcity;//城市b int cost; //建成a到b的路的花費 bool operator < (const Edge &q) const//注意返回值的型別,運算子過載。 { return cost<q.cost; } }; Edge edge[10000]; class UFSet { public: UFSet(int nsize) { size = nsize; parent = new int[size+1]; }; ~UFSet() { delete[] parent; parent = NULL; }; void makeSet(int n);////初始化每個元素的祖先 int findSet(int x);//找到元素x的祖先元素 void unionSet(int a, int b);//若兩個元素的祖先不同,則將x元素的祖先設定為y元素的祖先 int getMinCost(int m);//獲取最小花費 private: int *parent;//存放祖先節點,例如x=parent[i],元素i的祖先節點為元素x int size; }; void UFSet::makeSet(int n) //初始化 { //初始化每一個元素都各自為一個獨立的集合,其祖先均設定為自身 for (size_t i = 1; i <= n; i++) parent[i] = i; } int UFSet::findSet(int x) { //找到元素所在的集合,也就是找到自己的最高的祖先, //這也是判斷兩個元素是否在同一個集合中的主要依據。 if (parent[x] == x)//遞迴截止條件(最高祖先的祖先是其自身) return x; parent[x] = findSet(parent[x]);//遞迴,最終找到x的最高祖先,並且沿途找到所有的最高祖先 return parent[x]; } void UFSet::unionSet(int x, int y) { //將x和y所在的集合進行合併,利用findSet()判斷x和y所在的集合是否相同, //如果不同,則要把其中一個元素的祖先指向另一個元素的祖先。 int ux = findSet(x);//獲取節點x的祖先 int uy = findSet(y); if (ux != uy) parent[ux] = uy; } int UFSet::getMinCost(int m) { sort(edge, edge + m);//必須先對邊排序(根據邊的修建費用),這樣才能貪心的形成最小花費 int sum = 0; for (int i = 0; i<m; i++) { int baseA = findSet(edge[i].acity);//找到城市a的祖先(要麼是自身要麼是城市b的編號) int baseB = findSet(edge[i].bcity); if (baseA != baseB) { parent[baseA] = baseB;//將城市a的祖先設定成b的祖先這個式子等價於parent[edge[i].acity] = edge[i].bcity sum += edge[i].cost; } } return sum; } int main() { int n = 0; while (cin >> n, n > 0) { int m = n*(n - 1) / 2; UFSet uset(10000); uset.makeSet(n);//初始化每個城市的祖先為自身 for (int i = 0; i < m; i++) cin>> edge[i].acity>> edge[i].bcity>> edge[i].cost; int mincost = uset.getMinCost(m); cout << mincost << endl; } return 0; }
練習四,繼續暢通工程
- 題目描述:
- 省政府“暢通工程”的目標是使全省任何兩個村莊間都可以實現公路交通(但不一定有直接的公路相連,只要能間接通過公路可達即可)。現得到城鎮道路統計表,表中列出了任意兩城鎮間修建道路的費用,以及該道路是否已經修通的狀態。現請你編寫程式,計算出全省暢通需要的最低成本。
- 輸入:
-
測試輸入包含若干測試用例。每個測試用例的第1行給出村莊數目N ( 1< N < 100 );隨後的 N(N-1)/2 行對應村莊間道路的成本及修建狀態,每行給4個正整數,分別是兩個村莊的編號(從1編號到N),此兩村莊間道路的成本,以及修建狀態:1表示已建,0表示未建。
當N為0時輸入結束。
- 輸出:
- 每個測試用例的輸出佔一行,輸出全省暢通需要的最低成本。
- 樣例輸入:
-
3 1 2 1 0 1 3 2 0 2 3 4 0 3 1 2 1 0 1 3 2 0 2 3 4 1 3 1 2 1 0 1 3 2 1 2 3 4 1 0
- 樣例輸出:
-
3 1 0
#include "algorithm"
#include <iostream>
#include "stack"
#include <cmath>
#include <set>
using namespace std;
class Edge
{
public:
int acity;//城市a
int bcity;//城市b
int cost; //建成a到b的路的花費
bool isBuild; //標記路是否建成
bool operator < (const Edge &q) const//注意返回值的型別,運算子過載。
{
return cost<q.cost;
}
};
Edge edge[100];
class UFSet
{
public:
UFSet(int nsize)
{
size = nsize;
parent = new int[size+1];
};
~UFSet()
{
delete[] parent;
parent = NULL;
};
void makeSet(int n);////初始化每個元素的祖先
int findSet(int x);//找到元素x的祖先元素
void unionSet(int a, int b);//若兩個元素的祖先不同,則將x元素的祖先設定為y元素的祖先
int getMinCost(int m);//獲取最小花費
private:
int *parent;//存放祖先節點,例如x=parent[i],元素i的祖先節點為元素x
int size;
};
void UFSet::makeSet(int n) //初始化
{
//初始化每一個元素都各自為一個獨立的集合,其祖先均設定為自身
for (size_t i = 1; i <= n; i++)
parent[i] = i;
}
int UFSet::findSet(int x)
{
//找到元素所在的集合,也就是找到自己的最高的祖先,
//這也是判斷兩個元素是否在同一個集合中的主要依據。
if (parent[x] == x)//遞迴截止條件(最高祖先的祖先是其自身)
return x;
parent[x] = findSet(parent[x]);//遞迴,最終找到x的最高祖先,並且沿途找到所有的最高祖先
return parent[x];
}
void UFSet::unionSet(int x, int y)
{
//將x和y所在的集合進行合併,利用findSet()判斷x和y所在的集合是否相同,
//如果不同,則要把其中一個元素的祖先指向另一個元素的祖先。
int ux = findSet(x);//獲取節點x的祖先
int uy = findSet(y);
if (ux != uy)
parent[ux] = uy;
}
int UFSet::getMinCost(int m)
{
sort(edge, edge + m);//必須先對邊排序(根據邊的修建費用),這樣才能貪心的形成最小花費
int sum = 0;
for (int i = 0; i<m; i++)
{
int baseA = findSet(edge[i].acity);//找到城市a的祖先(要麼是自身要麼是城市b的編號)
int baseB = findSet(edge[i].bcity);
if (baseA != baseB)
{
parent[baseA] = baseB;//將城市a的祖先設定成b的祖先這個式子等價於parent[edge[i].acity] = edge[i].bcity
sum += edge[i].cost;
}
}
return sum;
}
int main()
{
int n = 0;
while (cin >> n, n > 0)
{
int m = n*(n - 1) / 2;
UFSet uset(100);
uset.makeSet(n);//初始化每個城市的祖先為自身
for (int i = 0; i < m; i++)
{
cin>> edge[i].acity>> edge[i].bcity>> edge[i].cost>> edge[i].isBuild;
if (edge[i].isBuild == 1)
uset.unionSet(edge[i].acity, edge[i].bcity);//將已經建成的兩個城市編號建立連線
}
int mincost = uset.getMinCost(m);
cout << mincost << endl;
}
return 0;
}