题目:
有N个点和M条边,每条边都有一个花费cost。求出一些边使得:
1 这些边的总花费最大。
2 所有点连通,即任意节点都可以到达所有节点。
3 不存在环路。
如果不能连通所有点,则输出-1
解题思路:
该题类似于最小生成树。可使用并查集和贪心算法解决。
1 将所有边按cost从大到小排序。
2 将N个点初始化为N个并查集。
3 从左到右遍历边数组,合并当前边连接的两个节点所在的并查集,如果两个节点位于同一个集合则不合并,如果发生了合并,将该边的花费累加到总花费中。如果合并后的集合节点数目达到了N,则完成了所有点的连通,循环结束。
4 如果所有节点都位于同一个并查集,则输出最大花费,否则输出-1。
代码:
#include<stdio.h>
typedef struct
{
int a,b; //节点a和b
int cost; //花费
} route;
int father[1001];
int rank[1001];
int mem[1001];
route edges[20000];
int cmp(const void *a, const void *b)
{
return ((route*)b)->cost - ((route*)a)->cost;
}
void make_set(int x)
{
father[x]=x;
rank[x]=0;
mem[x]=1;
}
//查找x所在并查集的根节点编号
int find_set(int x)
{
if(father[x] != x) father[x]=find_set(father[x]);
return father[x];
}
//合并a节点和b节点所在的并查集,返回合并后集合的节点数,属于同一个集合返回0
int union_set(int a, int b)
{
int result;
a=find_set(a);
b=find_set(b);
if(a==b) return 0;
if(rank[a] < rank[b])
{
father[a]=b;
mem[b] += mem[a];
result=mem[b];
}
else
{
if(rank[a]==rank[b]) rank[a]++;
father[b]=a;
mem[a]+=mem[b];
result=mem[a];
}
return result;
}
int main()
{
int n,m,i,result,ans=0;
scanf("%d%d", &n, &m);
for(i=0; i<m; i++) scanf("%d%d%d", &edges[i].a, &edges[i].b, &edges[i].cost);
//初始化并查集
for(i=1; i<=n; i++) make_set(i);
//对边排序
qsort(edges, m, sizeof(route), cmp);
for(i=0; i<m; i++)
{
result=union_set(edges[i].a, edges[i].b);
if(result) ans += edges[i].cost;
if(result == n) break; //已经连接所有节点
}
if(result==n)
printf("%d\n", ans);
else printf("%d\n", -1);
return 0;
}