题
思路
树的遍历+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第一次在博客写题解,大佬轻喷.