分组背包+树形DP(BY LPX)

1 篇文章 0 订阅

专题传送门:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=24217#overview

A.POJ 1155

/*
类型:树形DP
题意:广播一场比赛,有发射站和用户,用户为叶子节点。每个发射站有消耗,到达用户有收益,现在问在不赔钱的情况下怎样使用户最大
思路:dp[i][j] 为给第i个节点分配k个用户的收益(可以为负),显然使dp[1][j]为正的最大j为所求
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<algorithm>

#define maxn 3100
#define maxm 1100
#define INF (1 << 28)
#define TP int
#define LL long long
#define DB double
#define Mid(x) ((x) >> 1)
#define Lc(x) ((x) << 1)
#define Rc(x) (((x) << 1)|1)
#define Clear(a,b) memset(a,b,sizeof(a))

using namespace std;

inline TP Max(TP a,TP b)
{
    if (a > b) return a;
    return b;
}

inline TP Min(TP a,TP b)
{
    if (a > b) return b;
    return a;
}

bool cmp(TP a,TP b)
{
    return a < b;
}

//BEGIN

struct EDGE{
    int v,w;
    };

bool cmp1(EDGE a,EDGE b)
{
    return a.w > b.w;
}

int dp[maxn][maxn],
    val[maxn],
    son[maxn];
vector <EDGE> links[maxn];

int dfs1(int k)
{
    int i,mm;
    mm = links[k].size();
    if (mm == 0) son[k] = 1;
    else son[k] = 0;
    for(i = 0;i < mm;i++)
        son[k] += dfs1(links[k][i].v);
    return son[k];
}

void dfs2(int k)
{
    //printf("%dxxx\n",k);
    int mm = links[k].size(),
        i,ii,jj,now_limt = 0;

    dp[k][0] = 0;
    if (mm == 0) {
        dp[k][1] = val[k];
        }
    for(i = 0;i < mm;i++)
        dfs2(links[k][i].v);
    for(i = 0;i < mm;i++) //分组背包,枚举组
        for(ii = (now_limt += son[links[k][i].v]);ii >= 1;ii--)
            for(jj = 1;jj <= ii && jj <= son[links[k][i].v];jj++)
                dp[k][ii] = Max(dp[k][ii],dp[k][ii-jj] + dp[links[k][i].v][jj] - links[k][i].w);
    //
    //printf("%d:\n",k);for(i = 1;i <= son[k];i++) printf("%d ",dp[k][i]);printf("\n");
    return ;
}

int Work()
{
    int n,m,tran,i,j,t;
    EDGE tmp;
    scanf("%d%d",&n,&m);
    for(i = 0;i < maxn;i++)
        for(j = 0;j < maxn;j++)
            dp[i][j] = -INF;
    Clear(son,0);
    tran = n - m;
    for(i = 1;i <= tran;i++) {
        scanf("%d",&t);
        for(j = 1;j <= t;j++){
            scanf("%d%d",&tmp.v,&tmp.w);
            links[i].push_back(tmp);
            }
        //sort(links[i].begin(),links[i].end(),cmp1);
        }
    for(;i <= n;i++)
        scanf("%d",&val[i]);
    dfs1(1);                    //第一遍DFS统计每个点的子树的点数
    //for(i = 1;i <= n;i++)
    //    printf("%d_%d\n",i,son[i]);
    dfs2(1);                    //第二遍DFS开始DP
   // for(i = 1;i <= m;i++)
   //     printf("%d %d\n",i,dp[1][i]);
    int ans = m;
    while(dp[1][ans] < 0) ans--;
    printf("%d\n",ans);
    return 0;
}

int main()
{
    //freopen("test.in","r",stdin);
    Work();
    return 0;
}

B. HDU 1011

/*
类型:树形DP
题意:一个虫子巢穴为树形结构,每个房间都有一些虫子和一定的概率存在脑虫。现在给你一定的战士,1战士可以消灭20个虫子,让你使打到脑虫的可能性最大
思路:dp[i][j]代表打i子树要j战士时的最大可能
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<algorithm>

#define maxn 210
#define maxm 110
#define INF (1 << 28)
#define TP int
#define LL long long
#define DB double
#define Mid(x) ((x) >> 1)
#define Lc(x) ((x) << 1)
#define Rc(x) (((x) << 1)|1)
#define Clear(a,b) memset(a,b,sizeof(a))

using namespace std;

inline TP Max(TP a,TP b)
{
    if (a > b) return a;
    return b;
}

inline TP Min(TP a,TP b)
{
    if (a > b) return b;
    return a;
}

bool cmp(TP a,TP b)
{
    return a < b;
}

//BEGIN

int m;
int dp[maxn][maxn],bug[maxn],sum_bug[maxn],boss[maxn];
vector <int> edge[maxn];

void dfs2(int k,int fa)
{
    //printf("%d\n",k);
    int mm = edge[k].size(),i,ii,jj;

    for(i = 0;i < mm;i++)
        if (edge[k][i] != fa)
            dfs2(edge[k][i],k);

    //if ((mm == 1 && fa > 0) || mm == 0)
    //    if (bug[k] <= m) dp[k][bug[k]] = boss[k];
    for(i = bug[k];i <= m;i++) dp[k][i] = boss[k];
    for(i = 0;i < mm;i++)
        if (edge[k][i] != fa){
            for(ii = m;ii >= bug[k];ii--)
                for(jj = 1;jj <= ii-bug[k];jj++)
                    dp[k][ii] = Max(dp[k][ii],dp[k][ii-jj] + dp[edge[k][i]][jj]);
            }
    //printf("%d: %d\n",k,boss[k]);
    //for(i = 0;i <= sum_bug[k];i++) printf("%d ",dp[k][i]);printf("\n");
    return ;
}

int Work()
{
    int n,i,tmp_u,tmp_v;
    scanf("%d%d",&n,&m);
    if (n == -1) return 0;

    for(i = 1;i <= n;i++)
        edge[i].clear();
    Clear(dp,0);
    Clear(sum_bug,0);
    Clear(bug,0);

    for(i = 1;i <= n;i++)
        scanf("%d%d",&bug[i],&boss[i]),bug[i] = (bug[i] + 19) / 20;
    //for(i = 1;i <= n;i++)   printf("%d ",bug[i]);printf("\n");
    for(i = 1;i < n;i++){
        scanf("%d%d",&tmp_u,&tmp_v);
        edge[tmp_u].push_back(tmp_v);
        edge[tmp_v].push_back(tmp_u);
        }

    if (m == 0) {
        printf("0\n");
        return 1;
        }

    dfs2(1,0);

    printf("%d\n",dp[1][m]);
    return 1;
}

int main()
{
    //freopen("test.in","r",stdin);
    while(Work());
    return 0;
}
//END

C. POJ 1947

/*
类型:树形DP
题意:给出一棵树,问最少切断几条边可以得到有p个结点的子树
思路:dp[i][j]代表第i个点为根的子树要得到j个节点的子树最少切多少个边
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<algorithm>

#define maxn 200
#define maxm 1100
#define INF (1000000)
#define TP int
#define LL long long
#define DB double
#define Mid(x) ((x) >> 1)
#define Lc(x) ((x) << 1)
#define Rc(x) (((x) << 1)|1)
#define Clear(a,b) memset(a,b,sizeof(a))

using namespace std;

inline TP Max(TP a,TP b)
{
    if (a > b) return a;
    return b;
}

inline TP Min(TP a,TP b)
{
    if (a > b) return b;
    return a;
}

bool cmp(TP a,TP b)
{
    return a < b;
}

//BEGIN

int p,ans;
int dp[maxn][maxn];
vector <int> edge[maxn];

void dfs(int k)
{
    int mm = edge[k].size(),
        i,ii,jj;

    if (k == 1) dp[k][1] = mm;
    else dp[k][1]  = mm + 1;
    dp[k][0] = 1;

    for(i = 0;i < mm;i++) {
        dfs(edge[k][i]);
        for(ii = p;ii >= 1;ii--)
            for(jj = 1;jj <= ii-1;jj++)
                dp[k][ii] = Min(dp[k][ii],dp[k][ii-jj] + dp[edge[k][i]][jj] - 2);
        }
    //printf("%3d:",k);for(i = 0;i <= p;i++) printf("%9d",dp[k][i]);printf("\n");
    if (dp[k][p] < ans) ans = dp[k][p];
    return ;
}

int Work()
{
    int n,i,fa,son,j;
    scanf("%d%d",&n,&p);
    for(i = 0;i <= n;i++)
        for(j = 0;j <= p;j++)
            dp[i][j] = INF;
    for(i = 1;i < n;i++) {
        scanf("%d%d",&fa,&son);
        edge[fa].push_back(son);
        }
    ans = INF;
    dfs(1);
    printf("%d\n",ans);
    return 0;
}

int main()
{
    //freopen("test.in","r",stdin);
    Work();
    return 0;
}

//END

D. HDU 1561

/*
类型:树形DP
题意:取M个城堡,使那啥最大
思路:不说了,桑心
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<algorithm>

#define maxn 250
#define maxm 1100
#define INF (1 << 28)
#define TP int
#define LL long long
#define DB double
#define Mid(x) ((x) >> 1)
#define Lc(x) ((x) << 1)
#define Rc(x) (((x) << 1)|1)
#define Clear(a,b) memset(a,b,sizeof(a))

using namespace std;

inline TP Max(TP a,TP b)
{
    if (a > b) return a;
    return b;
}

inline TP Min(TP a,TP b)
{
    if (a > b) return b;
    return a;
}

bool cmp(TP a,TP b)
{
    return a < b;
}

//BEGIN

int n,m;
int dp[maxn][maxn],val[maxn];
vector <int> son[maxn];

void dfs(int k)
{
    int mm = son[k].size(),
        i,ii,jj;
    dp[k][1] = val[k];

    for(i = 0;i < mm;i++) {
        dfs(son[k][i]);
        for(ii = m;ii >= 1;ii--)
            for(jj = 0;jj <= ii-1;jj++)
                dp[k][ii] = Max(dp[k][ii],dp[k][ii-jj] + dp[son[k][i]][jj]);
        }
    //printf("%d: ",k);for(i = 0;i <= n;i++) printf("%d ",dp[k][i]);printf("\n");
    return ;
}

int Work()
{
    int a,b,i;
    scanf("%d%d",&n,&m);
    if (n == 0) return 0;
    m++;

    Clear(dp,0);
    Clear(val,0);

    for(i = 0;i <= n;i++)
        son[i].clear();
    for(i=  1;i <= n;i++){
        scanf("%d%d",&a,&val[i]);
        son[a].push_back(i);
        }
    //for(i = 1;i <= n;i++) printf("%d ",val[i]);printf("\n");
    dfs(0);
    printf("%d\n",dp[0][m]);
    return 1;
}

int main()
{
   // freopen("test.in","r",stdin);
    while(Work());
    return 0;
}
//END

E. HDU 1561

/*
类型:树形DP
题意:给出一棵树,投放K个机器人,问使全部点都被走过的情况下机器人走的总路程和最小。
思路:dp[i][j]代表第i个点为根的子树要得到j个节点的子树最少切多少个边
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<algorithm>

#define maxn 11000
#define maxm 12
#define INF (1 << 28)
#define TP int
#define LL long long
#define DB double
#define Mid(x) ((x) >> 1)
#define Lc(x) ((x) << 1)
#define Rc(x) (((x) << 1)|1)
#define Clear(a,b) memset(a,b,sizeof(a))

using namespace std;

inline TP Max(TP a,TP b)
{
    if (a > b) return a;
    return b;
}

inline TP Min(TP a,TP b)
{
    if (a > b) return b;
    return a;
}

bool cmp(TP a,TP b)
{
    return a < b;
}

//BEGIN

struct EDGE{
    int ed,w;
    } tmp;

int n,k;
int dp[maxn][maxm];
vector <EDGE> edge[maxn];

void dfs(int now,int fa)
{
    int mm = edge[now].size(),
        i,ii,jj,next;

    for(i = 0;i < mm;i++)
        if (edge[now][i].ed != fa) {
            next = edge[now][i].ed;
            dfs(next,now);

            for(ii = k;ii >= 0;ii--) {
                dp[now][ii] += dp[next][0] + edge[now][i].w * 2;
                for(jj = 0;jj <= ii;jj++)
                    dp[now][ii] = Min(dp[now][ii],dp[now][ii-jj] + dp[next][jj] + edge[now][i].w * jj);
                }
            }
    return ;
}

int Work()
{
    int i,j,s,u,v,w;
    if (scanf("%d%d%d",&n,&s,&k) == EOF) return 0;
    for(i = 1;i <= n;i++) edge[i].clear();
    Clear(dp,0);
    for(i = 1;i < n;i++) {
        scanf("%d%d%d",&u,&v,&w);
        tmp.w = w;
        tmp.ed = v;
        edge[u].push_back(tmp);
        tmp.ed = u;
        edge[v].push_back(tmp);
        }
    dfs(s,0);
    printf("%d\n",dp[s][k]);
    return 1;
}

int main()
{
    //freopen("test.in","r",stdin);
    while(Work());
    return 0;
}

F. POJ 2486

/*
树形DP
题意:一颗树上吃苹果,每个点上都有一定的苹果
思路:状态dp[i][j][k]表示为第i个点走j步,k为0代表不回,k为1代表回则有三种转移,见代码。
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<algorithm>
#include<math.h>

#define maxn 110
#define maxm 210
#define INF (1 << 28)
#define TP int
#define LL long long
#define DB double
#define Mid(x) ((x) >> 1)
#define Lc(x) ((x) << 1)
#define Rc(x) (((x) << 1)|1)
#define Clear(a,b) memset(a,b,sizeof(a))

using namespace std;

inline TP Max(TP a,TP b)
{
    if (a > b) return a;
    return b;
}

inline TP Min(TP a,TP b)
{
    if (a > b) return b;
    return a;
}

bool cmp(TP a,TP b)
{
    return a < b;
}

//BEGIN
int k,n;
int apple[maxn],
    dp[maxn][maxm][2];
vector<int>edge[maxn];

void dfs(int x,int fa)
{
    int mm,i,j,jj,son;
    //0-不回 1-回
    mm = edge[x].size();
    for(i = 0;i < mm;i++)
        if (edge[x][i] != fa)
            dfs(edge[x][i],x);
    for (j = 0;j <= k;j++)
        dp[x][j][1] = dp[x][j][0] = apple[x];
    for(i = 0;i < mm;i++)
        if ((son = edge[x][i]) != fa)
            for(j = k;j >= 0;j--) //又他妈傻逼了!!!!!!
                for(jj = 0;jj <= j;jj++) {
                    if (jj >= 2) dp[x][j][0] = Max(dp[x][j][0],dp[x][j-jj][0]+dp[son][jj-2][1]);
                    if (jj >= 1) dp[x][j][0] = Max(dp[x][j][0],dp[x][j-jj][1]+dp[son][jj-1][0]);
                    if (jj >= 2) dp[x][j][1] = Max(dp[x][j][1],dp[x][j-jj][1]+dp[son][jj-2][1]);
                    }
    return ;
}

int Work()
{
    int i,u,v;
    if (scanf("%d%d",&n,&k) == EOF) return 0;
    Clear(dp,0);
    Clear(apple,0);
    for(i = 1;i <= n;i++) scanf("%d",&apple[i]);
    for(i = 1;i <= n;i++) edge[i].clear();
    for(i = 1;i < n;i++) {
        scanf("%d%d",&u,&v);
        edge[u].push_back(v);
        edge[v].push_back(u);
        }
    dfs(1,0);
    printf("%d\n",dp[1][k][0]);
}

int main()
{
    //freopen("test.in","r",stdin);
    while(Work());
    return 0;
}


//END

J. HDU 4276

/*
树形DP
题意:
思路:
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<algorithm>
#include<math.h>

#define maxn 110
#define maxm 510
#define OO (1 << 28)
#define TP int
#define LL long long
#define DB double
#define Mid(x) ((x) >> 1)
#define Lc(x) ((x) << 1)
#define Rc(x) (((x) << 1)|1)
#define Clear(a,b) memset(a,b,sizeof(a))

using namespace std;

inline TP Max(TP a,TP b)
{
    if (a > b) return a;
    return b;
}

inline TP Min(TP a,TP b)
{
    if (a > b) return b;
    return a;
}

bool cmp(TP a,TP b)
{
    return a < b;
}

//BEGIN

struct EDGE{
    int ed,cost;
    } tmp;

vector<EDGE>edge[maxn];

int n,t;
int dp[maxn][maxm],gold[maxn];

int dfs_find(int now,int fa)
{
    int mm,i,time;
    if (now == n) return 0;
    mm = edge[now].size();
    for(i = 0;i < mm;i++)
        if (edge[now][i].ed != fa) {
            time = dfs_find(edge[now][i].ed,now) + edge[now][i].cost;
            if (time >= 0) {
                    edge[now][i].cost = 0;
                    return time;
                    }
            }
    return -OO;
}

void dfs(int now,int fa)
{
    int mm,i,son,cost,j,jj;
    mm = edge[now].size();
    for(j = 0;j <= t;j++) dp[now][j] = gold[now];
    for(i = 0;i < mm;i++)
        if (edge[now][i].ed != fa) {
            son = edge[now][i].ed;
            dfs(son,now);
            cost = edge[now][i].cost;
            for(j = t;j >= 0;j--)
                for(jj = cost*2;jj <= j;jj++)
                    dp[now][j] = Max(dp[now][j] , dp[now][j-jj] + dp[son][jj-cost*2]);
            }
    return ;
}

int Work()
{
    int i,u,v,w;
    if (scanf("%d%d",&n,&t) == EOF) return 0;
    Clear(dp,0);
    Clear(gold,0);
    for(i = 1;i <= n;i++)
        edge[i].clear();
    for(i = 1;i < n;i++) {
        scanf("%d%d%d",&u,&v,&w);
        tmp.cost = w;
        tmp.ed = u;
        edge[v].push_back(tmp);
        tmp.ed = v;
        edge[u].push_back(tmp);
        }
    for(i = 1;i <= n;i++)
        scanf("%d",&gold[i]);
    t -= dfs_find(1,0);
    //printf("%dXXX\n",t);
    if (t <= 0) {
        printf("Human beings die in pursuit of wealth, and birds die in pursuit of food!\n");
        return 1;
        }
    dfs(1,0);
    printf("%d\n",dp[1][t]);
    return 1;
}


int main()
{
    //freopen("test.in","r",stdin);
    while(Work());
    return 0;
}


//END


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值