poj 1155 树状dp + 背包

链接:http://poj.org/problem?id=1155

题意:播放电视信号,信号塔和用户构成了一棵树,有n-m个信号塔,n-m+1~n是用户= =,两个节点之间传输需要花钱,问在电视台不亏损的情况下最多多少个用户能收看。。

思路:传说中的树状dp= =,dp  好难的样子。。这道题的状态转移方程请见代码(其实是懒得写了。。)。刚开始我想错了,后来想了想发现不对。。额,dp弱,没办法=。=

dp[i][j]表示i节点有j个人收看(就是给j个人信号)时最大盈利,用当前节点的儿子节点来更新,大概就是这样了。。看不懂我写的很正常~因为我也不懂。233333

附上一个博客。。http://blog.csdn.net/woshi250hua/article/details/7644959(建图自我感觉没我的方法好(其实是我习惯我这种建图方式了))

这个题用的内存好多。。MLE了两次,然后接着百度题解去。。

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn = 3010;
const int inf = 0x3f3f3f3f;
struct Side{int v,w,next;}side[maxn*3];
int node[maxn],top;
void add_side(int u,int v,int w){
    side[top]=(Side){v,w,node[u]};
    node[u]=top++;
}

int n,m;
int mon[maxn];//记录每个用户准备交多少保护费
int dp[maxn][maxn],vis[maxn],sum[maxn];
//dp[i][j]记录第i个节点拥有j个用户时最大盈利
//vis记录是否访问过。。sum记录当前节点拥有几个叶子节点(叶子节点也就是用户)
void init(){
    memset(node,-1,sizeof(node));
    //memset(dp,0,sizeof(dp));
    for(int i=0;i<maxn;i++)
        for(int j=0;j<maxn;j++)
            dp[i][j] = -inf;
    memset(vis,0,sizeof(vis));
    memset(sum,0,sizeof(sum));
    top=0;
}
void dfs(int u){
    if(vis[u])return ;
    vis[u] = 1;
    dp[u][0] = 0;
    int tot=0;
    int p = node[u];
    while(p!=-1){
        tot++;
        dfs(side[p].v);
        sum[u] += sum[side[p].v];
        p = side[p].next;
    }
    if(tot==0){
        sum[u] = 1;
        dp[u][1] = mon[u];
    }
    else {
        for(int i = node[u];i!=-1;i = side[i].next){
            for(int j = sum[u];j>=1;j--){
                for(int k = sum[side[i].v];k>=1;k--){
                    if(j>=k&&dp[u][j-k]!=-inf&&dp[side[i].v][k]!=-inf)
                        dp[u][j] = max(dp[u][j] ,dp[u][j-k] + dp[side[i].v][k] - side[i].w);
                }
            }
        }
    }
}

int main(){
    while(~scanf("%d%d",&n,&m)){
        init();
        for(int i=1;i<=n-m;i++){
            int all;
            scanf("%d",&all);
            while(all--){
                int v,w;
                scanf("%d%d",&v,&w);
                add_side(i,v,w);
            }
        }
        for(int i=n-m+1;i<=n;i++)
            scanf("%d",&mon[i]);
        dfs(1);
        for(int i=n;i>=0;i--)
            if(dp[1][i]>=0){printf("%d\n",i);break;}
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值