3 3 0 1 1 1 2 1 2 0 1 4 5 0 1 1 1 2 1 2 3 1 3 0 1 0 2 2 0 0
3 5
题意:即求伪森林。给一个图,n个点,m条边,每个连通分量最多有一个环,求最大值。
即求出一个最大的子图(子图的每个连通分量最多有一个环)
注意:类似于求最大生成树,但并不是,不能求最大生成树+最大边,给一个别人博客上写的例子:
6 7
0 1 9
0 2 6
1 2 8
3 4 5
4 5 5
3 5 4
2 4 1
这组数据如果是错误的方法就是34,选择(0,1),(1,2),(0,2),(2,4),(3,4),(4,5)
正确的方法答案是37,选择(0,1),(1,2),(0,2),(3,4),(3,5),(4,5)
正确思路:在最大生成树的基础上判断一下环的问题,按照权值从大到小去边,然后按条件合并
1.两个子树都没有环直接合并
2.两个子树都有环不能合并
3.其中一个有环合并之后要同时标记一下
#include <iostream> #include <memory.h> #include <algorithm> #define NUM 10010 using namespace std; int n; int m; int father[NUM]; int circle[NUM]; //用于标记是否有环,有环为1,无环为0 struct Edge { int u,v,w; }e[NUM*10]; void init() { for(int i=0;i<n;i++) { father[i]=i; } } int find_father(int x) { if(x==father[x]) return x; else return father[x]=find_father(father[x]); } int cmp(Edge a,Edge b) { return a.w>b.w; } int Krual() { int ans=0; memset(circle,0,sizeof(circle)); for(int i=0;i<m;i++) { int fa=find_father(e[i].u); int fb=find_father(e[i].v); if(fa==fb) // { if(!circle[fa]) { circle[fa]=1; ans+=e[i].w; } continue; } if(circle[fa]&&circle[fb]) continue; if(circle[fa]&&!circle[fb]) father[fb]=fa; else father[fa]=fb; ans+=e[i].w; } return ans; } int main() { while(cin>>n>>m) { if(n==0&&m==0) break; init(); for(int i=0;i<m;i++) { cin>>e[i].u>>e[i].v>>e[i].w; } sort(e,e+m,cmp); cout<<Krual()<<endl; } return 0; }