1. 程式人生 > >Save your cats Aizu

Save your cats Aizu

傳送門

題意:

給出一個圖,這個圖形封閉區域(可能多個可能一個也沒有),問最少需要破壞多少邊使得封閉區域不封閉。先給出每個點的座標,然後給出每條邊的關係。

題解:

這個題因為封閉區域可能一個可能多個可能沒有,所以正向思維去做這個題會比較複雜,把一些邊破壞掉之後,得到的圖不在存在封閉區域,那麼破壞邊之後得到的圖就是一棵樹,所以,總的邊長度之和=要破壞的長度 + 破壞完後剩下的樹的邊的總長度,既然希望破壞的長度儘可能的小, 只需要讓得到的樹儘可能的大就可以了,這樣問題就轉化成了求一棵最大生成樹,用並查集來做,這樣這個問題就很簡單了。

附上程式碼:


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>

using namespace std;

const int N=11000;

struct node{
    double x,y;
};
node point[N];

double dist(node a,node b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

struct edge{
    int u,v;
    double d;
};
edge edges[N*(N+1)/2+5];

bool cmp(edge a,edge b)
{
    return a.d>b.d;
}

int n,m;
int pre[N];

int find(int x)
{
    if(x==pre[x]){
        return x;
    }else{
        pre[x]=find(pre[x]);
        return pre[x];
    }
}

void merge(int u,int v)
{
    int t1=find(u);
    int t2=find(v);
    if(t1!=t2){
        pre[t2]=t1;
    }
    return ;
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF){
        for(int i=1;i<=n;i++){
            pre[i]=i;
        }
        for(int i=1;i<=n;i++){
            scanf("%lf%lf",&point[i].x,&point[i].y);
        }
        double sum=0;
        int temp1,temp2;
        for(int i=0;i<m;i++){
            scanf("%d%d",&temp1,&temp2);
            edges[i].u=temp1;
            edges[i].v=temp2;
            edges[i].d=dist(point[temp1],point[temp2]);
            sum=sum+edges[i].d;
        }
        sort(edges,edges+m,cmp);
        double s=0;
        for(int i=0;i<m;i++){
            int x=find(edges[i].u);
            int y=find(edges[i].v);
            if(x!=y){
                merge(x,y);
                s=s+edges[i].d;
            }
        }
        double ans=0;
        ans=sum-s;
        printf("%.3f\n",ans);
    }
    return 0;
}