题意:给定一棵树,树的边带负权,树的叶子带正权,求一棵子树,要求涵盖尽可能多的叶子,同时保证总权值不为负
题解:树形DP,dp[i][j]代表以i为根的子树含有j个叶子结点时最大权值,对每一个i的子树,dp[i][j]=max(dp[i][j],dp[i][j-k]+dp[t][k]-c)
View Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 using namespace std; 6 const int N=3005,inf=1<<29; 7 int head[N],nc,n,m,dp[N][N],val[N],num[N]; 8 bool vis[N]; 9 struct Edge 10 { 11 int to,cost,next; 12 } edge[2000000]; 13 void add(int a,int b,int c) 14 { 15 edge[nc].to=b; 16 edge[nc].next=head[a]; 17 edge[nc].cost=c; 18 head[a]=nc++; 19 } 20 void dfs(int now) 21 { 22 if(head[now]==-1) 23 { 24 if(now>n-m) 25 { 26 num[now]=1; 27 dp[now][0]=0; 28 dp[now][1]=val[now]; 29 } 30 else 31 { 32 dp[now][num[now]=0]=0; 33 } 34 return; 35 } 36 else 37 { 38 num[now]=0; 39 dp[now][0]=0; 40 for(int i=head[now]; i!=-1; i=edge[i].next) 41 { 42 int t=edge[i].to,c=edge[i].cost; 43 dfs(t); 44 int tp=num[now]+num[t]; 45 for(int j=num[now]+1; j<=tp; j++) 46 dp[now][j]=-inf; 47 num[now]=tp; 48 tp=num[t]; 49 for(int j=num[now]; j>=0; j--) 50 { 51 for(int k=1; k<=tp&&k<=j; k++) 52 { 53 if(dp[now][j-k]!=-inf) 54 dp[now][j]=max(dp[now][j],dp[now][j-k]+dp[t][k]-c); 55 56 } 57 } 58 } 59 } 60 } 61 int main() 62 { 63 //freopen("data.txt","r",stdin); 64 while(scanf("%d%d",&n,&m)!=EOF) 65 { 66 memset(val,0,sizeof(val)); 67 memset(head,-1,sizeof(head)); 68 nc=0; 69 int nu=n-m; 70 for(int i=1; i<=nu; i++) 71 { 72 int k,a,c; 73 scanf("%d",&k); 74 for(int j=0; j<k; j++) 75 { 76 scanf("%d%d",&a,&c); 77 add(i,a,c); 78 } 79 } 80 for(int i=nu+1; i<=n; i++) 81 scanf("%d",&val[i]); 82 memset(dp,0,sizeof(dp)); 83 dfs(1); 84 for(int i=num[1]; i>=0; i--) 85 { 86 if(dp[1][i]>=0) 87 { 88 printf("%d\n",i); 89 break; 90 } 91 } 92 } 93 return 0; 94 }