首先,这道题目参考别人题解,因为一直认为这样做会超时;
以d[ i ] [ j ] 代表i节点为 服务器或 听众满足j个人的需求的最大收入;
状态转移很明显 d[ i ] [ j ] = max( d[ i ] [j ] , d[ i ][ j-k ]+d[ one_son ][ k ] - fee(fee为连通i和one_son这条边的花费) );
直接动归该方程是不可行的因为,在计算d[ i ][ j - k ]时,还会重新利用one_son,而因为d [ i ][ j ]已经对son节点做出选k个的决断;这样会构成选取的重复;
解决这个问题可以从一出发先解决子问题;然后集中精力解决该层的问题;对于这一层只要对每个孩子逐一判断,然后刷表解决;
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int inf = 10000000; const int maxn = 3100; struct node{ int to,val,next; }tree[maxn]; int head[maxn],d[maxn][maxn],temp[maxn],n,m,len,fee[maxn],num[maxn]; void init(){ memset(head,-1,sizeof(head)); memset(num,0,sizeof(num)); len = 0; } void add_eadge(int x,int y,int v){ tree[len].to=y; tree[len].val=v; tree[len].next=head[x]; head[x]=len++; } void dfs(int root){ if(root>n-m){ d[root][1]=fee[root]; num[root] = 1; return ; } for(int i=head[root];i!=-1;i=tree[i].next){ int p = tree[i].to; dfs(p); for(int j=0;j<=num[root];j++) temp[j]=d[root][j]; for(int j=0;j<=num[root];j++) for(int k=1;k<=num[p];k++){ d[root][k+j] = max(d[root][k+j],temp[j]+d[p][k]-tree[i].val); } num[root]+=num[p]; } } int main() { while(scanf("%d %d",&n,&m)==2){ init(); for(int i=1;i<=n-m;i++){ int x,y,z; scanf("%d",&x); for(int j=1;j<=x;j++){ scanf("%d %d",&y,&z); add_eadge(i,y,z); } } for(int i=n-m+1;i<=n;i++){ int x; scanf("%d",&x); fee[i] = x; } for(int i=0;i<=n;i++){ for(int j=0;j<=m;j++) d[i][j] = -inf; d[i][0] = 0; } dfs(1); int res = 0; for(int i=m;i>=0;i--){ if(d[1][i]>=0){ res = i; break; } } printf("%d\n",res); } return 0; }