JZOJ2678 树B

Description

已知无向连通图G由N个点,N-1条边组成。每条边的边权给定。现要求通过删除一些边,将节点1与另M个指定节点分开,希望删除的边的权值和尽量小,求此最小代价。

Input

每个输入文件中仅包含一个测试数据。

第一行包含两个整数N,M。

第二行至第N行每行包含3个整数,A、B、C,表示节点A与节点B有一条边相连,边权为C。

第N+1行至第N+M行每行包含一个整数X,表示要求与节点1分开的节点。

Output

输出文件仅包含一个整数,表示最小代价。

思路

 树的遍历+DP

不知道为什么大家都喜欢叫树形DP,好高逼的感觉.害得我昨天那题一听是什么"树形DP"就不敢写了

对于每个必删的点 i ,很容易想到我们不必遍历以其为根的子树,
  这样的情况花费是 cost[i]=E[V[i].dad->V[i]]

对于一个不必删的点 j ,
 删掉其子树中所有必删的点的花费是
   cost[j]=SUM(cost[儿子1]+cost[儿子2]+......+cost[儿子n]);
 我们可以考虑删掉以其为根的子树,这样当然就删除了其子树中的所有必删点
  花费 cost[j]=E[V[i].dad->V[i]]

考虑最小情况,所以点 n 的值应为 min(E[V[i].dad->V[i]],SUM(cost[儿子1]+cost[儿子2]+......+cost[儿子n]))

由于树的性质,每个点的DP值只给自己父亲用,所以不必存下,利用返回值带回即可.

代码(有问题欢迎讨论)

 1 #include <iostream>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 int N,M;
 6 
 7 struct edge{int fr,to,vl;}E[2000000];
 8 int cmpS(edge m,edge n){return (m.fr==n.fr)?(m.to<n.to):(m.fr<n.fr);}
 9 
10 struct node{int l,r,flag,vl;}V[1000001];
11 
12 int dfs(int dad,int dv,int id){//他老爸是谁(李刚) / 他到他老爸多远 / 他是谁
13     if(V[id].flag==1)//要删就删,不多话
14         return dv;
15     int s=0;//儿子的和SUM(没儿子返回0)
16     for(int i=V[id].l;i<=V[id].r;++i){
17         if(E[i].to!=dad){//你爸爸还是你爸爸,只能找自己儿子
18             s+=dfs(id,E[i].vl,E[i].to);
19         }
20     }
21     return min(dv,s);
22 }
23 
24 int main(){
25     
26 // 输入数据
27     cin>>N>>M;
28     for(int i=1;i<N;++i)//无向图存成两个有向边
29         cin>>E[i].fr>>E[i].to>>E[i].vl,E[N-1+i]={E[i].to,E[i].fr,E[i].vl};
30     for(int i=1,a;i<=M;++i)
31         cin>>a,V[a].flag=1;//标记要删
32 // 初始化图
33     for(int i=1;i<=N;++i)
34         V[i]={0,-1,V[i].flag,0};
35     sort(&E[1],&E[2*N-1],cmpS);
36     for(int i=1;i<=2*N-1;++i){
37         if(E[i].fr!=E[i-1].fr){
38             V[E[i].fr].l=i;
39             V[E[i-1].fr].r=i-1;
40         }
41     }
42 // 深搜/输出
43     cout<<dfs(0,1e9,1);//可怜的根节点没爸爸所以他离他爸很远(要不你当他爸做根节点//别打我)
44     
45     return 0;
46     
47 }
能理解尽量自己码

淼仔mxxr第一次在博客写题解,大佬轻喷.

原文地址:https://www.cnblogs.com/mxxr/p/11143103.html