HDU 4003 Find Metal Minaral 树上瞎搞分组背包

对于分组背包,每组选且只选一件商品的写法只想出了二维数组的写法。

dp[s][k] 表示 在前s类中选取价格为 k 的商品的最优解。

dp[s][k] = max( dp[s-1][k-product[s][j].c] + product[s][j].w)。dp[s][k]每次只会有dp[s-1]更新得到,保证了前s-1类商品的选取。

对于此题,可以把每棵子树的情况看成一类商品,递归求解。

dp[s][k]表示在s子树上投入k个机器人时的最优解。

当k == 0时,表示在s点投入1个机器人,且此机器人又返回s点,当k >= 1时,表示在s点投入k个机器人且都不返回的s结点时的最优解。

更新时显而易见的有,每棵子树选取一种情况下最优解来更新根节点的最优解。写法即为上面的二位分组背包,但是此题貌似范围较大建议滚动数组。

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <stack>
#include <map>

#pragma comment(linker, "/STACK:1024000000");
#define EPS (1e-8)
#define LL long long
#define ULL unsigned long long LL
#define _LL __int64
#define _INF 0x3f3f3f3f
#define Mod 1000000007
#define LM(a,b) (((ULL)(a))<<(b))
#define RM(a,b) (((ULL)(a))>>(b))

using namespace std;

const LL MAXN = 10010;

struct N
{
    int u,v,w,next;
} edge[MAXN*2];

int head[MAXN];

int Top;

void Link(int u,int v,int w = -1)
{
    edge[Top].u = u;
    edge[Top].v = v;
    edge[Top].w = w;
    edge[Top].next = head[u];
    head[u] = Top++;
}

void Init_head_Top(int n)
{
    memset(head,-1,sizeof(int)*(n+2));
    Top = 0;
}

int dp[12][MAXN];

int dfs(int s,int pre,int k)
{
    if(dp[k][s] != -1)
        return dp[k][s];

    int val[2][14];

    memset(val,-1,sizeof(val));

    int site = 1;

    bool mark = false;

    for(int p = head[s]; p != -1; p = edge[p].next)
    {
        if(edge[p].v != pre)
        {
            if(mark == false)
            {
                mark = true;

                memset(val[site&1],-1,sizeof(val[site&1]));

                for(int j = 0; j <= k; ++j)
                {
                    int t = dp[j][edge[p].v] == -1 ? dfs(edge[p].v,s,j) : dp[j][edge[p].v];

                    if(j == 0)
                        val[site&1][j] = t + edge[p].w*2;
                    else
                        val[site&1][j] = t + edge[p].w*j;
                }
            }
            else
            {
                memset(val[site&1],-1,sizeof(val[site&1]));

                for(int i = 0; i <= k; ++i)
                {
                    if(val[(site-1)&1][i] != -1)
                    {
                        for(int j = k; j >= 0; --j)
                        {
                            if(i+j <= k)
                            {
                                int temp;

                                int t = dp[j][edge[p].v] == -1 ? dfs(edge[p].v,s,j) : dp[j][edge[p].v];

                                if(j == 0)
                                    temp = t + edge[p].w*2 + val[(site-1)&1][i];
                                else
                                    temp = t + edge[p].w*j + val[(site-1)&1][i];
                                if(val[site&1][i+j] == -1 || val[site&1][i+j] > temp)
                                    val[site&1][i+j] = temp;
                            }

                        }
                    }
                }
            }
            site++;
        }
    }

    for(int i = 0;i <= k; ++i)
    {
        if(val[(site-1)&1][i] != -1)
            dp[i][s] = val[(site-1)&1][i];
       else dp[i][s] = 0;
    }

  //  cout<<"k = "<<k<<" s = "<<s<<" dp = "<<dp[k][s]<<endl;

    return dp[k][s];
}

int main()
{
    int n,s,k;

    int u,v,w;

    while(scanf("%d %d %d",&n,&s,&k) != EOF)
    {
        Init_head_Top(n);

        for(int i = 0; i <= k; ++i)
        {
            memset(dp[i],-1,sizeof(int)*(n+2));
        }

        for(int i = 1; i < n; ++i)
        {
            scanf("%d %d %d",&u,&v,&w);
            Link(u,v,w);
            Link(v,u,w);
        }

        dfs(s,-1,k);

        int Min = dp[1][s];

        for(int i = 1; i <= k; ++i)
        {
            Min = min(Min,dp[i][s]);
           // cout<<"dp = "<<dp[i][s]<<endl;;
        }

        printf("%d\n",Min);
    }

    return 0;
}

HDU 4003 Find Metal Minaral 树上瞎搞分组背包

时间: 2024-07-30 23:54:24

HDU 4003 Find Metal Minaral 树上瞎搞分组背包的相关文章

POJ 2468 Apple Tree 树上瞎搞分组背包

昨晚Debug了好久始终找不出哪里错了,今早再一看发现自己已荣升逗比Beta 2.0 Version. 个人感觉此题为HDU 4003 的弱化版. 把每棵子树都看成一类商品,在每类商品中至多选一件.则问题转化为最基本的分组背包问题. dp[s][c][k] c == 1时,表示在s结点不返回时走K的最大收益,c == 0时,表示在s结点重新返回时走k步的最大收益. 可以dfs从底到顶更新dp.值得一提的是,要保证每个结点的dp[s][c][k]只会被dfs一次,即不能重复更新. 因为重复的dfs

hdu 4003 Find Metal Mineral 【树形dp,分组背包】

题目:hdu 4003 Find Metal Mineral 题意:火星上发现了一些n个矿厂,有 k 个机器人从 s 点出发采矿,给出路段间的花费cost,求最小的花费采所有的矿. 分类:树形dp + 分组背包 分析:结论1:假如我们从 i点出发k个机器人采完以 k 为根节点的所有矿又回到 i 点,那么花费为 i 为根节点的cost 的和 乘以 k. 对于每个节点,定义状态:dp[i][j] 用 j 个机器人去采以 i 为根节点的子树的所有矿石的最小花费 状态转移方程:dp[father][nu

HDU 4003 - Find Metal Mineral

Find Metal Mineral Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others)Total Submission(s): 3368    Accepted Submission(s): 1569 Problem Description Humans have discovered a kind of new metal mineral on Mars which are d

HDU 6125 Free from square(状态压缩+分组背包)

http://acm.hdu.edu.cn/showproblem.php?pid=6125 题意: 在${1,2,3,...n}$的数中选择1~k个数,使得它们的乘积不能被平方数整除(1除外),计算有多少种取法. 思路: 考虑一下他们的质因子,如果两个数有相同的质因子,那么它们两个肯定是不能同时选的.这是不是很像分组背包,但是如果以质因子来分类的话,一个数就可能处于多个背包当中,这样就不行了,因为一个数你只能在一个背包中. 这题的数据范围很小,在500的范围内,质数的平方数小于500的就8个数

hdu 4003 Find Metal Mineral 树形DP

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4003 Humans have discovered a kind of new metal mineral on Mars which are distributed in point‐like with paths connecting each of them which formed a tree. Now Humans launches k robots on Mars to collect

HDU 4003 Find Metal Mineral (树形DP,树形分组背包,经典)

题意:给定一棵树图,n个节点,有边权,要派k<11个机器人从节点s出发,遍历所有的点,每当1只机器人经过1条边时就会花费该边的边权,边是可重复走的.问遍历完所有点的最小花费? 思路: 非常经典,首先需要知道一点,才能往下推理.就是“如果在t点派c个机器人往孩子u,那么最多只有1个机器人能走会回来到t,否则花费总是不划算的”. 稍微证明一下: (1)假设派1个机器人往u,逛一圈回到u的父亲t,花费v= 子树u的边权和*2 + e(t,u)*2.若机器人不要了,那花费肯定比v还要少. (2)假设派2

HDU 5128 The E-pang Palace(暴力瞎搞)

题目大意:给你n个点,让你组成矩形,然后如果有两个矩形不相交的话就计算他们的面积,求最大的两个矩形的面积并.注意的是回字型的嵌套,面积的并是最大的矩形的面积. 解题思路:暴力,枚举出来矩形,然后再暴力枚举两个矩形判断是否相交,是否为回字型. The E-pang Palace Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 512000/512000 K (Java/Others) Total Submission(s): 288 

hdu 1712 ACboy needs your help (dp 分组背包)

#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int a[200][200]; int dp[200]; int main() { int n,m; int i,j,k; while(scanf("%d%d",&n,&m)!=EOF) { if(n==0&&m==0

HDU-4003 Find Metal Mineral (树形DP+分组背包)

题目大意:用m个机器人去遍历有n个节点的有根树,边权代表一个机器人通过这条边的代价,求最小代价. 题目分析:定义状态dp(root,k)表示最终遍历完成后以root为根节点的子树中有k个机器人时产生的总代价.则状态转移方程为: dp(root,k)=min(dp(root,k),dp(son,j)+dp(root,k-j)+j*w(root,son))  j>0 要注意,当j为0的时候表示遍历完son这个子树后所有的机器人都回到root.可以证明,如果让遍历son的所有的机器人都回到root,那