POJ 1155 TELE (树形DP+分组背包)
http://poj.org/problem?id=1155
题意:有n个点构成一棵树,节点1是广播站,2~n-m 是中转站,n-m+1~n是用户,从广播站传递信息,每经过一条边都有花费,不同的用户收听广播会支付不同的费用,
问广播站在不亏本的情况下,最多有多少用户可以收听到广播
解析:dp[i][j]表示i为根节点下j个用户收听的盈利(可以为负值)状态转移方程dp[u][i]=max(dp[u][i],dp[u][i-j]+dp[v][j]-edge[e].w);
有题解说是01背包,但是我觉得从子节点到父节点转移就是分组背包,也是按分组背包处理的
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<string> #define inf (1<<28) #define nMAX 3005 using namespace std; int n,m; int dp[nMAX][nMAX],num[nMAX],val[nMAX],head[nMAX],s_edge; int max(int a,int b) { return a>b?a:b; } struct Edge { int v,w,nxt; }edge[nMAX]; void addedge(int u,int v,int w) { s_edge++; edge[s_edge].v=v; edge[s_edge].w=w; edge[s_edge].nxt=head[u]; head[u]=s_edge; } //预处理,求出num 标记叶子节点 void dfs1(int u) { if(u>=n-m+1){num[u]=1;return ;}//叶子节点 num[u]=0; for(int e=head[u];e;e=edge[e].nxt) { int v=edge[e].v; dfs1(v); num[u]+=num[v]; } } void dfs2(int u) { int i,j; if(u>=n-m+1) {dp[u][1]=val[u]; return;} for(int e=head[u];e;e=edge[e].nxt) { int v=edge[e].v; dfs2(v); for(i=num[u];i>=0;i--)//分组背包 for(j=1;j<=num[v];j++) { if(i>=j) dp[u][i]=max(dp[u][i],dp[u][i-j]+dp[v][j]-edge[e].w); } } } int main() { int i,j,k,v,w; while(~scanf("%d%d",&n,&m)) { memset(head,0,sizeof(head)); s_edge=0; for(i=1;i<=n-m;i++) { scanf("%d",&j); while(j--) { scanf("%d%d",&v,&w); addedge(i,v,w); } } for(i=n-m+1;i<=n;i++) scanf("%d",&val[i]); memset(num,0,sizeof(num)); dfs1(1);//预处理 for(i=1;i<=n;i++) for(j=0;j<=n;j++) { if(j==0) dp[i][j]=0; else dp[i][j]=-inf; } dfs2(1); int ans=0; for(i=0;i<=num[1];i++) if(dp[1][i]>=0) ans=i; printf("%d\n",ans); } return 0; }