uvalive4015 (树上背包)

给一棵树,边上有权值,然后给一个权值x,问从根结点出发, 走不超过x的距离,最多能经过多少个结点。

走过的点可以重复走,所以可以从一个分支走下去,然后走回来,然后再走另一个分支

dp[u][j][0] 表示从u出发,走了j个点,然后不回到u点的最小花费

dp[u][j][1] 表示从u出发,走了j个点,然后回到u点的最小花费

dp[u][j][0] = min(dp[u][j][0], dp[v][k][0]+dp[u][j-k][1]+dis, dp[v][k][1]+dp[u][j-k][0]+2*dis);

可能是当前这个分支不回到u点,那么就是dp[v][k][0] + dp[u][j-k][1] +dis

可能是当前这个分支回到u点(那么u->v的边走两次,那么就是2*dis),但是以前的分支不回到u点,dp[v][k][1] + dp[u][j-k][0] + 2*dis

dp[u][j][1] = min(dp[u][j][1], dp[v][k][1]+ dp[u][j-k][1] + dis)

#pragma warning(disable:4996)
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <bitset>
#include <algorithm>
#include <iostream>
#include <string>
#include <functional>
const int INF = 1 << 30;

/*

树形背包
*/
const int N = 500 + 10;
struct Edge
{
    int to, dis, next;
}g[N*2];
int head[N], e, fa[N];
int dp[N][N][2];
int size[N];
int n, u, v, dis, query[1111];
void addEdge(int u, int v, int dis)
{
    g[e].to = v;
    g[e].dis = dis;
    g[e].next = head[u];
    head[u] = e++;
}
void init()
{
    memset(dp, 0x7f7f7f7f, sizeof(dp));
    memset(head, -1, sizeof(head));
    memset(fa, -1, sizeof(fa));
    e = 0;
}
void dfs(int u, int fa)
{
    dp[u][1][0] = dp[u][1][1] = 0;
    size[u] = 1;
    for (int i = head[u];i != -1;i = g[i].next)
    {
        int v = g[i].to;
        if (v == fa) continue;
        dfs(v, u);
        size[u] += size[v];
        for (int j = size[u];j >= 1;--j)
            for (int k = 1;k <= size[v]; ++k)
            {
                dp[u][j][0] = std::min(dp[u][j][0], std::min(dp[u][j-k][1]+dp[v][k][0] + g[i].dis, dp[u][j-k][0]+dp[v][k][1] + g[i].dis * 2));
                dp[u][j][1] = std::min(dp[u][j][1], dp[u][j-k][1] + dp[v][k][1] + g[i].dis * 2);
            }

    }
}
int main()
{
    int tcase = 1;
    while (scanf("%d", &n) ,n)
    {
        init();
        for (int i = 1;i < n;++i)
        {
            scanf("%d%d%d", &u, &v, &dis);
            addEdge(v, u, dis);
            fa[u] = v;
        }
        int root;
        for (int i = 0;i < n;++i)
            if (fa[i] == -1)
                root = i;
        int q, x;
        dfs(root, -1);
        scanf("%d", &q);
        printf("Case %d:\n", tcase++);
        while (q--)
        {
            int ans;
            scanf("%d", &x);
            for (int i = 1;i <= n;++i)
                if (dp[root][i][0] <= x || dp[root][i][1] <= x)
                    ans = i;
            printf("%d\n", ans);
        }
    }
    return 0;
}
时间: 2024-12-21 11:40:58

uvalive4015 (树上背包)的相关文章

【BZOJ】4033: [HAOI2015]树上染色 树上背包

[题目]#2124. 「HAOI2015」树上染色 [题意]给定n个点的带边权树,要求将k个点染成黑色,使得 [ 黑点的两两距离和+白点的两两距离和 ] 最大.n<=2000. [算法]树上背包 [题解]设f[i][j]表示子树i中有j个黑点对答案的贡献(包括点 i 到父亲的边 p ),由于边p的贡献只和 j 有关,所以最后再统计. 所以做树上背包即可,注意这题特殊在f[x][0]≠0,所以初始f[x][k]+=f[y][0],然后不要把0作为物品. 最后统计边p的贡献:w[p] *(子树内黑点

luogu 2014 选课 树上背包

树上背包 #include<bits/stdc++.h> using namespace std; const int N=310; const int inf=0x3f3f3f3f; vector<int> son[N]; int f[N][N],s[N],n,m; void dfs(int u){ f[u][0]=0; for(int i=0;i<son[u].size();i++){ int v=son[u][i]; dfs(v); for(int j=m;j>0

HihoCoder 1055 刷油漆 (树上背包)

题目:https://vjudge.net/contest/323605#problem/A 题意:一棵树,让你选择m个点的一个连通块,使得得到的权值最大 思路:树上背包,我们用一个dp数组,dp[i][j] ,代表以i为根时的选其子树j个节点所得到的最大值,然后我们对于每个以i为根我们当做有m件物品,然后对于不同的子树当作不同的分组即可 #include<bits/stdc++.h> #define maxn 105 #define mod 1000000007 using namespac

P2014 选课 (树上背包)

树上背包: 树形背包就是原始的树上动归+背包,一般用来处理以一棵树中选多少点为扩展的一类题,基本做法与树上dp无异,不过在状态转移方程中会用到背包的思想. 它基本上是这个样子的: 存图),然后dfs进去补全子节点的信息,f数组的意思是以fa为中转点,找出fa往下的可取1~j个点时各自的最大收益. void dfs(int fa){ for(int i=0;i<son[fa].size();i++){ int ny=son[fa][i]; dfs(ny); for(int j=m+1;j>=1;

hdu2415(树上背包)

这道题好像没什么人写题解,于是写了一发 题意:有个坏蛋想要参加竞选,需要得到m个人的支持,买通第i个人(1<=i<=n)需要一个cost[i],同时这些人又有上下属关系,只要买通了领导,他的下属(可以是下属的下属)也会给你投票,问最少要花多少钱 数据范围:N<=200,m<=n; 其实就是比较裸的树上分组背包,每个子节点的背包是一个组,选自己也是一个组,dfs搞一搞就好了,然而字符串处理实在恶心..... #include<string> #include<cst

LuoGu P2014选课(人生第一个树上背包)

(著名哲学家沃兹基硕德曾经说过:“$QuickSilverX$ $is$ $a$ $BB$”) 就是课与课可能有一些优先关系 这种关系我们可以通过图论建模来解决 不难发现,若将优先选修课向当前课连边,就会生成森林(每门课只有一个选修课,也就只有一条入边) 将所有无入边(没有优先课)的结点与0相连,形成树 ~~不难~~发现这是一个树上DP与背包... 树本身就是个递归的结构,我们每个结点的状态肯定是先递归处理儿子结点的状况来转移的 定义状态 $F[i][j][k]$ 表示第 $i$ 的前 $j$

hdu 1561 The more, The Better (树上背包)

The more, The Better Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 5516    Accepted Submission(s): 3280 Problem Description ACboy很喜欢玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中ACboy允许攻克M个城堡并获得里面的宝物

【树形背包】bzoj4033: [HAOI2015]树上染色

仔细思考后会发现和51nod1677 treecnt有异曲同工之妙 Description 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并 将其他的N-K个点染成白色.将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益. 问收益最大值是多少. Input 第一行两个整数N,K. 接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to). 输入保证所有点之间是联通的. N<=2

【题解】 bzoj4033: [HAOI2015]树上染色* (动态规划)

bzoj4033,懒得复制,戳我戳我 Solution: 定义状态\(dp[i][j]\)表示\(i\)号节点为根节点的子树里面有\(j\)个黑色节点时最大的贡献值 然后我们要知道的就是子节点到根节点这条边会计算次数就是:子树中白色节点数\(*\)子树外白色节点数\(+\)子树中黑色节点数\(*\)子树外黑色节点数 \[dp[u][j+k]=max(dp[u][j+k],\] \[dp[u][j]+dp[v][k]+(1ll)*k*(m-k)*dis[v]+(1ll)*(siz[v]-k)*(n