题目链接:https://vjudge.net/problem/Aizu-2224
题目大意:
先给出 N 个点的坐标(x,y),这N个点之间有且只有M条边,接下来给出 M 条边的两端点,每条边对应的边权就是两端点的距离,断开一条边所需的花费就是这条边的边权。现在我们要断开一些边,使得剩余的边没有办法围成圈,但又要使得总花费最小。求所需的最小花费。
解题思路:
要是剩余的边没有办法围成圈,那么其实就是想要让剩余的边能够组成一棵树,所需的总花费其实就是除了这棵树以外的边的所有边权,所以我们让这颗树的权值最大,那么总花费(= 所有边的边权 - 树的总权值)就会最小。利用正难则反的思路,我们把这个问题转变成一道求最大生成树裸题。
Danger ! 记得考虑重边!
Danger!数组要开得足够大!
AC代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <algorithm> 5 using namespace std; 6 const int maxn=10000+5; 7 int fa[maxn]; 8 struct edge{ 9 int u,v; 10 double cost; 11 }es[maxn*maxn]; 12 struct node{ 13 int x,y; 14 }cord[maxn]; 15 bool cmp(const edge &a,const edge &b){ 16 return a.cost>b.cost; 17 } 18 double cal(node a,node b){ 19 return sqrt((double)(a.x-b.x)*(a.x-b.x)+(double)(a.y-b.y)*(a.y-b.y)); 20 } 21 int finds(int rt){ 22 if(fa[rt]==rt) return rt; 23 return fa[rt]=finds(fa[rt]); 24 } 25 bool same(int a,int b){ 26 return finds(a)==finds(b); 27 } 28 int main(){ 29 int N,M; 30 double tot=0.0; 31 scanf("%d%d",&N,&M); 32 for(int i=1;i<=N;i++) scanf("%d%d",&cord[i].x,&cord[i].y); 33 for(int i=0;i<M;i++){ 34 scanf("%d%d",&es[i].u,&es[i].v); 35 double c=cal(cord[es[i].u],cord[es[i].v]); 36 es[i].cost+=c; 37 tot+=c; 38 } 39 sort(es,es+M,cmp); 40 for(int i=1;i<=N;i++) fa[i]=i; 41 double res=0.0; 42 for(int i=0;i<M;i++){ 43 edge e=es[i]; 44 if(!same(e.u,e.v)){ 45 int t1=finds(e.u),t2=finds(e.v); 46 fa[t1]=t2; 47 res+=e.cost; 48 } 49 } 50 printf("%lf\n",tot-res); 51 return 0; 52 }