1. 程式人生 > >貪心策略練習與總結

貪心策略練習與總結

練習三,還是暢通工程

題目描述:
    某省調查鄉村交通狀況,得到的統計表中列出了任意兩村莊間的距離。省政府“暢通工程”的目標是使全省任何兩個村莊間都可以實現公路交通(但不一定有直接的公路相連,只要能間接通過公路可達即可),並要求鋪設的公路總長度為最小。請計算最小的公路總長度。
輸入:

    測試輸入包含若干測試用例。每個測試用例的第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;
}