- 题目大意
有一个V个结点M条边的带边权无向平面图,有一个人在一个区域,要拆一些墙使得他可以到达任意一个区域,问最小花费。
- 解题思路
先开始想不通,看了别人突然恍然大悟,根据欧拉公式我们可以知道对于一个有k个连通分量的平面图的区域数r=E−V+k+1。那么那么对偶图生成树的边数为r−1=E−V+k,这些边也就是要删除的原图中的边,那么要留下的边数就是V−k这刚好就是原图每个连通分量生成树的边数之和。考虑保留原图每个连通分量的生成树,显然满足要求。题目要求花费最小,也就是留下的边权值最大,那么我们直接对每个连通分量求最大生成树即可,删除的边数就是总边数减去生成树的边数和,最小花费就是全部边的花费减去最大生成树的花费。
- 代码
#include<algorithm> #include<cstdio> using namespace std; const int MAX=1e6; int fa[MAX]; long long sum,tmp,sum1; void init(int n) { for(int i=1;i<=n;i++) { fa[i]=i; } } struct Edge { int u, v, w; bool operator<(const Edge &rhs)const { return w > rhs.w; } }e[MAX]; int find(int x) { if(x==fa[x]) return x; else return fa[x]=find(fa[x]); } bool Union(int x,int y) { int fx=find(x),fy=find(y); if(fx==fy) return false; fa[fx]=fy; return true; } void kruskal(int m) { sort(e,e+m); for(int i=0;i<m;i++) { int u=e[i].u,v=e[i].v,w=e[i].w; if(Union(u,v)) { sum+=w; tmp--; } } } int main() { int n,m,x,y; while(scanf("%d%d",&n,&m)!=EOF) { init(n); sum1=0; for(int i=0;i<n;i++) scanf("%d%d",&x,&y); for(int i=0;i<m;i++) { scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); sum1+=e[i].w; } tmp=m,sum=0; kruskal(m); printf("%lld %lld ",tmp,sum1-sum); } return 0; }