poj 1155 TELE
题目描述:...
解法:
树形dp+分组背包。
dp[i][j]记录以i为根的子树分配j个用户时的最大收益,cnt[i]记录以i为根的子树有几个叶子节点,对于每个i,背包容量即为cnt[i]的大小。
将i的每个子节点看作一组,这组背包的物品重量即为1~cnt[son[i]],由于只能从每组中选出一种物品,所以可以看做分组背包。
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #define N 3010 6 using namespace std; 7 const int inf=(1<<30); 8 struct Edge{ 9 int next,len; 10 }; 11 vector<Edge>V[N]; 12 int dp[N][N]; 13 int cnt[N]; 14 void init(){ 15 for(int i=1;i<N;i++)V[i].clear(); 16 for(int i=0;i<N;i++) 17 for(int j=0;j<N;j++) 18 dp[i][j]=-inf; 19 for(int i=0;i<N;i++) 20 dp[i][0]=0; 21 memset(cnt,0,sizeof(cnt)); 22 } 23 int dfs(int n){ 24 if(cnt[n]!=0)return cnt[n]; 25 cnt[n]=0; 26 for(int i=0;i<V[n].size();i++){ 27 dfs(V[n][i].next); 28 cnt[n]+=cnt[V[n][i].next]; 29 } 30 return cnt[n]; 31 } 32 void DP(int n){ 33 if(V[n].size()==0) 34 return ; 35 for(int i=0;i<V[n].size();i++){ 36 DP(V[n][i].next); 37 for(int k=cnt[n];k>=1;k--) 38 for(int j=1;j<=cnt[V[n][i].next];j++) 39 if(k>=j) 40 dp[n][k]=max(dp[n][k],dp[n][k-j]+dp[V[n][i].next][j]+V[n][i].len); 41 42 } 43 } 44 int main(){ 45 int n,m; 46 while(cin>>n>>m){ 47 init(); 48 for(int i=1;i<=n-m;i++){ 49 int k,a,c; 50 cin>>k; 51 while(k--){ 52 cin>>a>>c; 53 Edge E; 54 E.next=a;E.len=-c; 55 V[i].push_back(E); 56 } 57 } 58 for(int i=n-m+1,j=1;i<=n;i++,j++){ 59 int c; 60 cin>>c; 61 dp[i][1]=c; 62 cnt[i]=1; 63 } 64 dfs(1); 65 DP(1); 66 for(int i=cnt[1];i>=1;i--) 67 if(dp[1][i]>=0){ 68 cout<<i<<endl; 69 break; 70 } 71 } 72 return 0; 73 }