POJ_2486_Apple Tree_树状DP

3 篇文章 0 订阅
你想吃苹果就吃,为什么要爬树。

题意

一棵N个点的树,每个点有一个值a[i],点编号为1~N,问从点1出发,一共走K步,共能获取多少a值。每个a值只能获取一次。

输入输出

Input
There are several test cases in the input
Each test case contains three parts.
The first part is two numbers N K, whose meanings we have talked about just now. We denote the nodes by 1 2 … N. Since it is a tree, each node can reach any other in only one route. (1<=N<=100, 0<=K<=200)
The second part contains N integers (All integers are nonnegative and not bigger than 1000). The ith number is the amount of apples in Node i.
The third part contains N-1 line. There are two numbers A,B in each line, meaning that Node A and Node B are adjacent.
Input will be ended by the end of file.

Note: Wshxzt starts at Node 1.

Output
For each test case, output the maximal numbers of apples Wshxzt can eat at a line.

分析

dp[i][j][k]表示以i为根的子树,在上面共走j步,k为1则最后必须回到i点,k为0则不需回到i点。
状态转移见代码。
有两个容易错的地方,一个是可能不需要走K步就可以得到最优解,然而DP没有进行到下标为K的点因此DP[1][K][]可能还是初始值。第二个是由于遍历一个点的儿子是有序的,我拉掉了先遍历到k值为0,后遍历到k值为1的情况,只要加上这种情况的状态转移即可,不需讨论儿子的排序。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define mxn 110
#define mxk 210
#define inf 0x3f3f3f3f
int n,K;
int a[mxn];
bool mapp[mxn][mxn];
int first[mxn],nxt[mxn],vv[mxn],cnt;
int dp[mxn][mxk][2];
void add(int u,int v){
    nxt[cnt]=first[u];
    first[u]=cnt;
    vv[cnt++]=v;
}
void build_tree(){
    bool flag[mxn];
    memset(flag,false,sizeof(flag));
    memset(first,-1,sizeof(first));
    cnt=0;
    int q[mxn];
    int head=0,tail=1;
    q[head]=1, flag[1]=true;
    while(head<tail){
        int now=q[head++];
        for(int i=1;i<=n;++i)   if(!flag[i]&&mapp[now][i]){
            flag[i]=true;
            q[tail++]=i;
            add(now,i);
        }
    }
}
void dfs(int now){
    dp[now][0][1]=a[now];
    for(int i=first[now];i!=-1;i=nxt[i]){
        dfs(vv[i]);
        for(int j=K;j>=0;--j){
            for(int k=0;k<j-1;++k){
                dp[now][j][1]=max(dp[now][j][1],dp[now][j-k-2][1]+dp[vv[i]][k][1]);
                dp[now][j][0]=max(dp[now][j][0],dp[now][j-k-2][0]+dp[vv[i]][k][1]);
                dp[now][j][0]=max(dp[now][j][0],dp[now][j-k-1][1]+max(dp[vv[i]][k][1],dp[vv[i]][k][0]));
            }
            dp[now][j][0]=max(dp[now][j][0],dp[now][0][1]+max(dp[vv[i]][j-1][1],dp[vv[i]][j-1][0]));
        }
    }
}
int DP(){
    for(int i=0;i<mxn;++i)
        for(int j=0;j<mxk;++j)
            for(int k=0;k<2;++k)
                dp[i][j][k]=-inf;
    dfs(1);
    int ret=0;
    for(int i=0;i<=K;++i)   ret=max(ret,max(dp[1][i][0],dp[1][i][1]));
    return ret;
}
int main(){
    while(scanf("%d%d",&n,&K)!=EOF){
        for(int i=1;i<=n;++i)   scanf("%d",a+i);
        memset(mapp,false,sizeof(mapp));
        for(int i=1;i<n;++i){
            int u,v;
            scanf("%d%d",&u,&v);
            mapp[u][v]=mapp[v][u]=true;
        }
        build_tree();
        printf("%d\n",DP());
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
POJ 2182是一道使用树状数组解决的题目,题目要求对给定的n个数进行排序,并且输出每个数在排序后的相对位置。树状数组是一种用来高效处理前缀和问题的数据结构。 根据引用中的描述,我们可以通过遍历数组a,对于每个元素a[i],可以使用二分查找找到a到a[i-1]中小于a[i]的数的个数。这个个数就是它在排序后的相对位置。 代码中的query函数用来求前缀和,add函数用来更新树状数组。在主函数中,我们从后往前遍历数组a,通过二分查找找到每个元素在排序后的相对位置,并将结果存入ans数组中。 最后,我们按顺序输出ans数组的元素即可得到排序后的相对位置。 参考代码如下: ```C++ #include <iostream> #include <cstdio> using namespace std; int n, a += y; } } int main() { scanf("%d", &n); f = 1; for (int i = 2; i <= n; i++) { scanf("%d", &a[i]); f[i = i & -i; } for (int i = n; i >= 1; i--) { int l = 1, r = n; while (l <= r) { int mid = (l + r) / 2; int k = query(mid - 1); if (a[i > k) { l = mid + 1; } else if (a[i < k) { r = mid - 1; } else { while (b[mid]) { mid++; } ans[i = mid; b[mid = true; add(mid, -1); break; } } } for (int i = 1; i <= n; i++) { printf("%d\n", ans[i]); } return 0; } ``` 这段代码使用了树状数组来完成题目要求的排序功能,其中query函数用来求前缀和,add函数用来更新树状数组。在主函数中,我们从后往前遍历数组a,通过二分查找找到每个元素在排序后的相对位置,并将结果存入ans数组中。最后,我们按顺序输出ans数组的元素即可得到排序后的相对位置。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [poj2182Lost Cows——树状数组快速查找](https://blog.csdn.net/aodan5477/article/details/102045839)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [poj_2182 线段树/树状数组](https://blog.csdn.net/weixin_34138139/article/details/86389799)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值