您的位置:首页 > 其它

POJ1155 TELE

2016-01-25 20:09 246 查看
描述:

有一个由发射器和客户端构成的树形结构,1到n-m为发射器,n-m+1到n为客户端,各边有权值表示连接这条边邻接的两点所需的花费,客户端有点权值表示客户愿意付出的钱数。问在整体不亏损(总money大于等于总花费)的情况下,最多能服务到多少个客户端

分析:

这里实际上是树形DP里的分组背包问题。把各节点看成一组背包。dp[i][j]表示以节点i为根节点的子树中,有j个客户端得到服务时所能得到的最大利润(总money减去总花费)。于是有dp[u][1]=Money(u为客户端),dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]-w)(u不为客户端,v为u的孩子节点,k为从孩子节点中选的客户数,w为u与v连边的权值)。

这样从下往上dfs即可。需要注意的是初始化问题,一般对于背包问题,求最大值时,对于无意义的情况(比如选取客户端数j大于i为根节点的子树内最大客户端数(即为sum[i])),这时就将其设为极小值(比如-INF)表示必定不会被选中,状态转移时也要注意一下。对于无意义的情况不作处理(即:if(dp[u][j-k]==-INF||dp[v][k]==-INF) continue;)。这样一开始就要把dp[i][0]=0,dp[i][1...n]=-INF。

代码:

#include<cstdio>
#include<cstring>
using namespace std;
#define N 3005
#define INF (1<<30)
#define min(x,y) (x<y?x:y)
#define max(x,y) (x<y?y:x)
int dp

,n,m,sum
,cnt;
struct Edge{
int to,w,next;
};
Edge edge[N<<1];int pedge
;
void addedge(int u,int v,int w){
edge[cnt].to=v;edge[cnt].w=w;
edge[cnt].next=pedge[u];pedge[u]=cnt++;
}
void dfs(int u,int fa){
int v,len;sum[u]=0;
for(int i=pedge[u];i!=-1;i=edge[i].next){
if((v=edge[i].to)==fa) continue;dfs(v,u);sum[u]+=sum[v];
for(int j=sum[u];j>0;--j){
len=min(j,sum[v]);
for(int k=1;k<=len;++k){
if(dp[u][j-k]==-INF||dp[v][k]==-INF) continue;
dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]-edge[i].w);
}
}
}
if(u>n-m) ++sum[u];
}
int main(){
int a,c,t,ans;
while(scanf("%d%d",&n,&m)!=EOF){
memset(pedge,-1,sizeof(pedge));cnt=0;
for(int i=1;i<=n;++i){
dp[i][0]=0;
for(int j=1;j<=n;++j) dp[i][j]=-INF;
}
for(int i=1;i<=n-m;++i){
scanf("%d",&t);
while(t--){
scanf("%d%d",&a,&c);
addedge(i,a,c);addedge(a,i,c);
}
}
for(int i=n-m+1;i<=n;++i){
scanf("%d",&t);dp[i][1]=t;
}
dfs(1,0);
for(int i=sum[1];i>0;--i){
if(dp[1][i]>=0) {ans=i;break;}
}
printf("%d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  poj 树形DP 分组背包