题目链接 http://poj.org/problem?id=2377
题目描述 :
有N个结点 M条边 并且知道每条边的权值
要建一个连接满足
(i)这些连接的总成本尽可能大
(ii)所有谷仓都连接在一起
(iii)使连接之间没有循环
很明显可以看出就是一个最原始的最小生成树,只不过这个权值最“小”变成了权值最大。(这个一定要注意)。
下面基于最小生成树的2种算法Prim 和 Kruskal给出2套解答(其实这就是个模版题)
Prim思想 —— “最优邻居一定在MST上”的贪心
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
//一些习惯的简化
#define el '\n'
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define lop(i, a, b) for(int i = (a); i < (b); i++)
#define dwn(i, a, b) for(int i = (a); i >= (b); i--)
#define ms(a, b) memset(a, b, sizeof(a))
const int MAXN = 1100;
const int INF = 0x3f3f3f3f;
int n, m;
struct edge{
int from, to;
int c;
}temp;
vector<edge> e[MAXN];
struct node{//定义大顶堆
int id, dist;
node(int a, int b){id = a, dist = b;}
bool operator<(const node & a)const{
return dist < a.dist;
}
};
int prim(int star){
int ans = 0;
int cnt = 0;
int dis[MAXN];//离已构成树的最大距离
bool vis[MAXN];//标记是否已连成树
priority_queue<node> Q; //大顶堆优先队列
rep(i, 0, n){
dis[i] = 0;
vis[i] = false;
}
Q.push(node(star, 0));
while(!Q.empty()){
int u = Q.top().id;
if(vis[u]){
Q.pop();
continue;
}
//cout << Q.top().id << " " << Q.top().dist << el << el;;
ans += Q.top().dist;
Q.pop();
vis[u] = true;
cnt++;
if(cnt == n)//n个结点已全部连成树
return ans;
lop(i, 0, e[u].size()){
int v = e[u][i].to, c = e[u][i].c;
if(dis[v] < c && !vis[v]) {//这里与最小生成树有所不同要特别注意
dis[v] = c;
//cout << v << " " << dis[v] << el << el;
Q.push(node(v, dis[v]));
}
}
}
return -1;//图不连通等问题,无法连成树
}
int main(){
cin >> n >> m;
rep(i, 1, m){//输入数据
int a, b, c;
cin >> a >> b >> c;
temp.to = b, temp.c = c;
e[a].push_back(temp);
temp.to = a;
e[b].push_back(temp);
}
cout << prim(1);
return 0;
}
Kruskal —— “最优边一定在MST上的”贪心
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
//一些习惯的简化
#define el '\n'
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define lop(i, a, b) for(int i = (a); i < (b); i++)
#define dwn(i, a, b) for(int i = (a); i >= (b); i--)
#define ms(a, b) memset(a, b, sizeof(a))
const int MAXN = 1100;
const int INF = 0x3f3f3f3f;
int n, m;
int fa[MAXN];//记录祖先结点
struct edge{
int x, y;//一条边接的两个点,无向边
int c;
bool operator>(const edge &a) const{
return c > a.c;
}
}e[MAXN * MAXN];
int find(int x){
return x == fa[x] ? x : find(fa[x]);//路径压缩
}
void join(int x, int y){
int x_root = find(x), y_root = find(y);
if(x_root != y_root)
fa[x_root] = y_root;//并集
}
int Kruskal(){//查并集 + 对边排序
int cnt = 0;
int ans = 0;
rep(i, 1, n){
fa[i] = i;
}
sort(e + 1, e + m + 1, greater<edge>());
rep(i, 1, m){
int u = e[i].x, v = e[i].y;
if(find(u) == find(v))
continue;
ans += e[i].c;
cnt++;
if(cnt == n - 1)
return ans;
join(u, v);
}
return -1;
}
int main(){
cin >> n >> m;
rep(i, 1, m){//输入数据
int a, b, c;
cin >> a >> b >> c;
e[i].x = a, e[i].y = b, e[i].c = c;//实际上这种算法不用管起点终点
}
cout << Kruskal();
return 0;
}