POJ 1155 TELE 树形DP(0-1背包)
题意:某电台要广播一场比赛,该电台网络是由n个网点组成的一棵树,其中m个点为客户端,
其余点为转发站。客户端i愿支付的钱为p[i],每一条边需要的花费固定,问电台在保证不亏损的情况下,
最多能使多少个客户端接收到信息?广播台所在的节点编号为1
分析:树形DP
1.用dp[u][j]表示从转发站u开始计算,满足其子树中j个顾客的最大收益
分析节点u的孩子节点v
a.放弃该孩子v,值不变
b.取该孩子的若干的节点:dp[u][j-k]+dp[u][k]-cost[i][j](cost[i][j]为连通该边所需要付出的代价。)
所以状态转移方程为:
dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]-cost[u][v]);
2.对于客户端i,dp[i][1] = p[i], 即客户端服务一个顾客获得p[i]的价值
3.最多的服务客服数为dp[1][i]中大于0的最大i值,即广播台服务i个客户不亏损.
4.设置数组num[i],表示结点i所能提供最大的用户个数,这一步属于剪枝操作,考虑到n = 3000,数据比较大,不剪枝会TLE。
View Code
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define INF 1<<29 #define maxn 3001 int dp[maxn][maxn],num[maxn]; //dp[i][j]数组表示结点i,它提供电视的用户个数j的情况下的最小费用 //num[i]表示结点i所能提供最大的用户个数 struct edge//邻接表 { int v, next, w; }edge[maxn]; int head[maxn],tot; void add(int s, int t, int w) { edge[tot].v=t; edge[tot].w=w; edge[tot].next=head[s]; head[s]=tot++; } void init()//邻接表初始化 { memset(head,-1,sizeof(head)); tot=0; } int k, m ,n; void dfs(int u) { int i, j, k; for(i=head[u];i!=-1;i=edge[i].next) { int v = edge[i].v; dfs(v); //把子节点的状态转移到父结点,自己推一下吧 for(j=num[u];j>=0;j--) for(k=1;k<=num[v];k++) dp[u][j+k]=max(dp[u][j+k],dp[u][j]+dp[v][k]-edge[i].w); num[u]+=num[v];//更新结点u所能提供的最大用户个数 } } int main() { int i, j, x ,y; while(~scanf("%d%d",&n,&m)) { init(); for(i=1;i<=n-m;i++) { scanf("%d",&k); while(k--) { scanf("%d%d",&x,&y); add(i,x,y); } } for(i=1;i<=n;i++)//dp[][]计算过程中会出现等于0的情况, for(j=1;j<=n;j++)//所以初始化要-INF,表示不通路 dp[i][j]=-INF; memset(num,0,sizeof(num)); for(i=n-m+1;i<=n;i++) { scanf("%d",&dp[i][1]); num[i]=1; } dfs(1); for(i=m;i>=0;i--) if(dp[1][i]>=0) { printf("%d\n",i); break; } } return 0; }


浙公网安备 33010602011771号