题意:给你一棵树,树的1号节点是电视台,2~n-m号节点是转播站,剩下的m个点是用户。点到点之间传递信号要消耗钱,最后一行给你能从每个用户收取的钱。然后问你在不亏本的情况下最多可以让多少的用户看到电视。
状态方程设为dp[i][j]表示,由点i开始传送,且让j名观众能够收到信号的情况下最大的利润
在搜索过程中,先把整棵树都跑完,处理出数组h[i] 表示以i为根节点的子树里有h[i]个观众。在递归到观众节点时,把该观众的收入存到dp[i][1]里。之后枚举i,j,k的值,分别表示走第几棵子树、递归到此最多能有几个观众、父亲节点下,该子树的前面的子树负责k个用户且这个子树负责j-k个用户。
如果j==k的话,表示不需要当前i这个子树来分担观众数,所以消耗是不需要加上父节点到该节点的那条花费的。最后遍历一遍,得到答案。
代码如下:
#include<iostream> #include<vector> #include<queue> #include<string.h> #include<stdio.h> using namespace std; #define p pair<int,int> const int inf=-2100000000; vector<p> a[100050]; int dp[3010][3010],c[3010],h[3010]; int ans,mark; int solve(int u) { if(a[u].size()==0) { dp[u][1]=c[u]; h[u]=1; return 0; } for(int i=0;i<a[u].size();i++) { solve(a[u][i].first); } int son=0; for(int i=0;i<a[u].size();i++) { for(int j=son+h[a[u][i].first];j>=1;j--) { for(int k=0;k<=son&&k<j;k++) { if(dp[u][k]!=inf&&dp[a[u][i].first][j-k]!=inf) { if(k!=j) dp[u][j]=max(dp[u][j],dp[u][k]+dp[a[u][i].first][j-k]-a[u][i].second); else dp[u][j]=max(dp[u][j],dp[u][k]+dp[a[u][i].first][j-k]); } } } son+=h[a[u][i].first]; } h[u]=son; } int main() { int i,j,k,l,x,y,n,huan,m; scanf("%d%d",&n,&m); for(i=1;i<=n-m;i++) { scanf("%d",&l); while(l--) { scanf("%d%d",&x,&y); a[i].push_back(p(x,y)); //cout<<a[i][a[i].size()-1].first<<" "<<a[i][a[i].size()-1].second<<endl; } }for(i=n-m+1;i<=n;i++) scanf("%d",&c[i]); for(i=1;i<=n;i++) { dp[i][0]=0; for(j=1;j<=m;j++) dp[i][j]=inf; } solve(1); int mx=0; for(i=0;i<=m;i++) if(dp[1][i]>=0) mx=i; cout<<mx<<endl; }