hdu4313 贪心并查集 || 树形dp

http://acm.hdu.edu.cn/showproblem.php?pid=4313

Problem Description

Machines have once again attacked the kingdom of Xions. The kingdom of Xions has N cities and N-1 bidirectional roads. The road network is such that there is a

unique path between any pair of cities.

Morpheus has the news that K Machines are planning to destroy the whole kingdom. These Machines are initially living in K different cities of the kingdom and

anytime from now they can plan and launch an attack. So he has asked Neo to destroy some of the roads to disrupt the connection among Machines. i.e after destroying those roads there should not be any path between any two Machines.

Since the attack can be at any time from now, Neo has to do this task as fast as possible. Each road in the kingdom takes certain time to get destroyed and they

can be destroyed only one at a time.

You need to write a program that tells Neo the minimum amount of time he will require to disrupt the connection among machines.

Input

The first line is an integer T represents there are T test cases. (0<T <=10)

For each test case the first line input contains two, space-separated integers, N and K. Cities are numbered 0 to N-1. Then follow N-1 lines, each containing three, space-separated integers, x y z, which means there is a bidirectional road connecting city x
and city y, and to destroy this road it takes z units of time.Then follow K lines each containing an integer. The ith integer is the id of city in which ith Machine is currently located.

2 <= N <= 100,000

2 <= K <= N

1 <= time to destroy a road <= 1000,000

Output

For each test case print the minimum time required to disrupt the connection among Machines.

Sample Input

1
5 3
2 1 8
1 0 5
2 4 5
1 3 4
2
4
0

Sample Output

10

Hint

Neo can destroy the road connecting city 2 and city 4 of weight 5 , and the road connecting city 0 and city 1 of weight 5. As only one road can be destroyed at a
time, the total minimum time taken is 10 units of time. After destroying these roads none of the Machines can reach other Machine via any path.
/**
hdu 4313   并查集+贪心
题目大意:
         给定n(n<=100000)个节点的树,每条边有一定的权值,有m个点是危险的,现在想将树分成m块使得每块中恰好只有一个危险的点,问最小的花费是多少。
解题思路:
         类似Kruskal的贪心过程,将节点按照从小到大排序,以每个危险的节点为并查集的根节点

*/
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;

const int maxn=100005;

struct note
{
    int u,v;
    LL w;
    bool operator < (const note &other) const
    {
        return w>other.w;
    }
} edge[maxn];

int father[maxn];
bool flag[maxn];
int n,m;

void init()
{
    for(int i=0; i<=n; i++)
    {
        father[i]=i;
    }
}

int find(int u)
{
    if(u==father[u])
        return u;
    return father[u]=find(father[u]);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0; i<n-1; i++)
        {
            int u,v;
            LL w;
            scanf("%d%d%I64d",&edge[i].u,&edge[i].v,&edge[i].w);
        }
        memset(flag,false,sizeof(flag));
        for(int i=0; i<m; i++)
        {
            int u;
            scanf("%d",&u);
            flag[u]=true;
        }
        init();
        LL sum=0;
        sort(edge,edge+n-1);
        for(int i=0; i<n-1; i++)
        {
            int x=find(edge[i].u);
            int y=find(edge[i].v);
            if(flag[x]&&flag[y])
            {
                sum+=edge[i].w;
            }
            else if(flag[x])
            {
                father[y]=x;
            }
            else
            {
                father[x]=y;
            }
        }
        printf("%I64d\n",sum);
    }
    return 0;
}
/**
hdu 4313  树形dp
题目大意:
         给定n(n<=100000)个节点的树,每条边有一定的权值,有m个点是危险的,现在想将树分成m块使得每块中恰好只有一个危险的点,问最小的花费是多少。
解题思路:
         dp[i][0]代表的是在当前以i节点为根节点的子树中,i所在的连通块中没有危险节点的最小花费;
         dp[i][1]代表的是在当前以i节点为根节点的子树中,i所在的连通块中有危险节点的最小花费;
         如果i是叶子节点:如果i为危险点dp[i][0] = inf,dp[i][1]= 0;否则dp[i][0] = 0,dp[i][1] = inf;
         如果i不是叶子节点:如果i是危险点dp[i][0] = inf , dp[i][1] = sigma min(dp[son][0],dp[son][1]+w);
         否则dp[i][0] = sigma min(dp[son][0],dp[son][1]+w),dp[i][1] = min(dp[i][0] – min(dp[son][0],dp[son][1]+w)+dp[son][1])。

*/
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;

const int maxn=100005;
const LL inf=0x3fffffffffffffffLL;

int head[maxn],ip;
LL dp[maxn][2];
int n,m;
bool flag[maxn];

struct note
{
    int v,next;
    LL w;
} edge[maxn*2];

void init()
{
    memset(head,-1,sizeof(head));
    ip=0;
}

void addedge(int u,int v,LL w)
{
    edge[ip].v=v,edge[ip].w=w,edge[ip].next=head[u],head[u]=ip++;
}

void dfs(int u,int pre)
{
  ///  printf("%d->%d\n",pre,u);
    if(flag[u])
    {
        dp[u][0]=inf;
        dp[u][1]=0;
        for(int i=head[u]; i!=-1; i=edge[i].next)
        {
            int v=edge[i].v;
            LL w=edge[i].w;
            if(v==pre)continue;
            dfs(v,u);
            dp[u][1]+=min(dp[v][0],dp[v][1]+w);
        }
        return;
    }
    else
    {
        dp[u][0]=0;
        dp[u][1]=inf;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            LL w=edge[i].w;
            if(v==pre) continue;
            dfs(v,u);
            dp[u][0]+=min(dp[v][0],dp[v][1]+w);
        }
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            LL w=edge[i].w;
            if(v==pre)continue;
            dp[u][1]=min(dp[u][1],dp[u][0]-min(dp[v][0],dp[v][1]+w)+dp[v][1]);
        }
        return;
    }
}
/**void dfs1(int u,int pre)
{
    printf("%d->%d\n",pre,u);
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(v==pre)continue;
        dfs1(v,u);
    }
}*/
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        init();
        for(int i=1; i<n; i++)
        {
            int u,v;
            LL w;
            scanf("%d%d%I64d",&u,&v,&w);
            u++;
            v++;
            addedge(u,v,w);
            addedge(v,u,w);
          //  printf("%d-->%d\n",u,v);
           // printf("%d-->%d\n",v,u);

        }
        memset(flag,false,sizeof(flag));
        for(int i=0; i<m; i++)
        {
            int x;
            scanf("%d",&x);
            x++;
            flag[x]=true;
        }
        dfs(1,-1);
        //dfs1(1,-1);
        /**for(int i=1;i<=n;i++)
        {
            printf("%d %I64d %I64d\n",i,dp[i][0],dp[i][1]);
        }*/
        printf("%I64d\n",min(dp[1][0],dp[1][1]));
    }
    return 0;
}

时间: 2024-12-15 07:09:13

hdu4313 贪心并查集 || 树形dp的相关文章

hdu 4514 并查集+树形dp

湫湫系列故事——设计风景线 Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Submission(s): 4539    Accepted Submission(s): 816 Problem Description 随着杭州西湖的知名度的进一步提升,园林规划专家湫湫希望设计出一条新的经典观光线路,根据老板马小腾的指示,新的风景线最好能建成环形,如果没有条件建成环形,

HDU 4514 湫湫系列故事——设计风景线(并查集+树形DP)

湫湫系列故事——设计风景线 Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total Submission(s): 4669    Accepted Submission(s): 853 Problem Description 随着杭州西湖的知名度的进一步提升,园林规划专家湫湫希望设计出一条新的经典观光线路,根据老板马小腾的指示,新的风景线最好能建成环形,如果没有条件建成环形

【bzoj3362/3363/3364/3365】[Usaco2004 Feb]树上问题杂烩 并查集/树形dp/LCA/树的点分治

题目描述 农夫约翰有N(2≤N≤40000)个农场,标号1到N,M(2≤M≤40000)条的不同的垂直或水平的道路连结着农场,道路的长度不超过1000.这些农场的分布就像下面的地图一样, 图中农场用F1..F7表示, 每个农场最多能在东西南北四个方向连结4个不同的农场.此外,农场只处在道路的两端.道路不会交叉且每对农场间有且仅有一条路径.邻居鲍伯要约翰来导航,但约翰丢了农场的地图,他只得从电脑的备份中修复了.每一条道路的信息如下: 从农场23往南经距离10到达农场17 从农场1往东经距离7到达农

(并查集+树形DP) hdu 4514

湫湫系列故事——设计风景线 Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Submission(s): 3269    Accepted Submission(s): 587 Problem Description 随着杭州西湖的知名度的进一步提升,园林规划专家湫湫希望设计出一条新的经典观光线路,根据老板马小腾的指示,新的风景线最好能建成环形,如果没有条件建成环形,

贪心 + 并查集 之 CODE[VS] 1069 关押罪犯 2010年NOIP全国联赛提高组

/* 贪心 + 并查集 之 CODE[VS] 1069 关押罪犯  2010年NOIP全国联赛提高组 两座监狱,M组罪犯冲突,目标:第一个冲突事件的影响力最小. 依据冲突大小,将M组罪犯按从大到小排序,按照排序结果,依次把每组罪犯分开放入两个监狱, 直到当前这组罪犯已经在同一个监狱中了,此时即为答案. 实现: 1)通过不在同一个监狱的罪犯,推断出在同一个监狱的罪犯.(依据:一共就两个监狱)      ftr[b] = a+n   // a和b是在不同监狱 ftr[c] = a+n   // a和

poj 1456 Supermarket (贪心+并查集)

# include <stdio.h> # include <algorithm> # include <string.h> using namespace std; int fa[10010]; struct node { int p; int d; }; struct node a[10010]; bool cmp(node a1,node a2)//利润从大到小 { return a1.p>a2.p; } int find(int x) { if(fa[x]

Codeforces 437D The Child and Zoo(贪心+并查集)

题目链接:Codeforces 437D The Child and Zoo 题目大意:小孩子去参观动物园,动物园分很多个区,每个区有若干种动物,拥有的动物种数作为该区的权值.然后有m条路,每条路的权值为该条路连接的两个区中权值较小的一个.如果两个区没有直接连接,那么f值即为从一个区走到另一个区中所经过的路中权值最小的值做为权值.问,平均两个区之间移动的权值为多少. 解题思路:并查集+贪心.将所有的边按照权值排序,从最大的开始连接,每次连接时计算的次数为连接两块的节点数的积(乘法原理). #in

Codeforces 437D 贪心+并查集

这个题目让我想起了上次在湘潭赛的那道跪死了的题.也是最值问题,这个也是,有n个动物园 每个都有权值 然后被m条路径相连接,保证图是连通的,然后求所有的p[i][j]之和.i,j为任意两个zoo,pij就为i到j路上遇到的包括i j在内的最小权值的zoo 然后我就焦头烂额了一下,这个明显就是看某个最小值为最后的结果发挥了多少次作用,但这个是图,要知道某个节点到底给多少条路径贡献出了最小值,还真是有点没头绪(在已知的复杂度一看 最多只能用nlogn的),最后是看了解答才知道的 用并查集来解决某个最小

POJ 1456 贪心 并查集

看一下中文版的题目就好,英文题目太晦涩了. 有两种方法可以解题 一种是贪心+优先队列 另一种是贪心+并查集 优先队列  需要说的都在代码注释里 #include<cstdio> #include<queue> #include<algorithm> using namespace std; struct s{ int day, val; }arr[100005]; bool cmp(s a, s b){ if(a.day != b.day) return a.day &