刷题总结——逐个击破(洛谷2700)

题目:

题目背景

三大战役的平津战场上,傅作义集团在以北平、天津为中心,东起唐山西至张家口的铁路线上摆起子一字长蛇阵,并企图在溃败时从海上南逃或向西逃窜。为了就地歼敌不让其逃走,***制定了先切断敌人东西两头退路然后再逐个歼灭敌人的战略方针。秉承伟大军事家的战略思想,作为一个有智慧的军长你,遇到了一个类似的战场局面。

题目描述

现在有N个城市,其中K个被敌方军团占领了,N个城市间有N-1条公路相连,破坏其中某条公路的代价是已知的,现在,告诉你K个敌方军团所在的城市,以及所有公路破坏的代价,请你算出花费最少的代价将这K个地方军团互相隔离开,以便第二步逐个击破敌人。

输入输出格式

输入格式:

 

第一行包含两个正整数n和k。

第二行包含k个整数,表示哪个城市别敌军占领。

接下来n-1行,每行包含三个正整数a,b,c,表示从a城市到b城市有一条公路,以及破坏的代价c。城市的编号从0开始。

 

输出格式:

 

输出一行一个整数,表示最少花费的代价。

 

输入输出样例

输入样例#1: 复制
5 3
1 2 4
1 0 4
1 3 8
2 1 1
2 4 3
输出样例#1: 复制
4

说明

【数据范围】

10%的数据:2≤n≤10;

100%的数据:2≤n≤100000,2≤k≤n,1≤c≤1000000。

题解:

  首先换个思维考虑:我们该保留哪些边?

  容易想到最大生成森林···最后保留的边的总和越大删除的就越小···

  将边从大到小排序后判下两边的点集中是否有特殊点即可···

  然而脑子进水刚开始想的时候没有想到并查集··只用在合并的时候让特殊点作为祖先即可·

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=1e5+5;
struct node{int x,y,v;}ed[N];
int n,m,father[N];
bool jud[N];
inline int R(){
    char c;int f=0;
    for(c=getchar();c<'0'||c>'9';c=getchar());
    for(;c<='9'&&c>='0';c=getchar())    f=(f<<3)+(f<<1)+c-'0';
    return f;
}
bool cmp(node a,node b){return a.v>b.v;}
inline int get(int a){return father[a]==a?a:(father[a]=get(father[a]));}
inline void comb(int a,int b){
    int fa=get(a),fb=get(b);
    if(!jud[fa]&&jud[fb])    father[fa]=fb;
    else father[fb]=fa;
}
int main(){
    //freopen("a.in","r",stdin);
    n=R(),m=R();int a;
    for(int i=1;i<=m;i++)    a=R(),jud[a]=true;
    for(int i=1;i<n;i++){
        ed[i].x=R(),ed[i].y=R(),ed[i].v=R();
    }
    sort(ed+1,ed+n,cmp);long long ans=0;
    for(int i=1;i<=n;i++)    father[i]=i;
    for(int i=1;i<n;i++){
        int x=ed[i].x,y=ed[i].y,v=ed[i].v;
        if(jud[get(x)]&&jud[get(y)]){
            ans+=v;
        }
        else  comb(x,y);
    }
    cout<<ans<<endl;
}

 

posted @ 2017-11-01 16:28  AseanA  阅读(732)  评论(0编辑  收藏  举报