hdu2415(树上背包)

这道题好像没什么人写题解,于是写了一发

题意:有个坏蛋想要参加竞选,需要得到m个人的支持,买通第i个人(1<=i<=n)需要一个cost[i],同时这些人又有上下属关系,只要买通了领导,他的下属(可以是下属的下属)也会给你投票,问最少要花多少钱

数据范围:N<=200,m<=n;

其实就是比较裸的树上分组背包,每个子节点的背包是一个组,选自己也是一个组,dfs搞一搞就好了,然而字符串处理实在恶心.....

#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
using namespace std;
const int MAX=1<<29;
int n,m;
map<string,int>dui;
vector<int>arr[205];
char str[100005],tmp[105];
int dp[205][205],fa[205],siz[205],cost[205],cnt=0;
void dfs1(int now)
{
    int i,j;
    siz[now]=1;
    for(i=0;i<arr[now].size();i++)
    {
        int v=arr[now][i];
        dfs1(v);
        siz[now]+=siz[v];
    }
}
void dfs(int now)
{
    int i,j,k;
    for(i=0;i<arr[now].size();i++)
    {
        dfs(arr[now][i]);
        int v=arr[now][i];
        for(j=m;j>=1;j--)for(k=1;k<=j;k++)dp[now][j]=min(dp[now][j],dp[now][j-k]+dp[v][k]);
    }
    if(now!=n+1)for(i=1;i<=min(m,siz[now]);i++)dp[now][i]=min(cost[now],dp[now][i]);
    return ;
}
int main()
{
    int i,j;
    char ss[10];
    while(scanf("%s",ss)!=EOF)
    {
        n=0;
        cnt=0;
        if(ss[0]==‘#‘)return 0;
        for(i=0;i<strlen(ss);i++)n*=10,n+=ss[i]-‘0‘;
        scanf("%d",&m);
        int i,j;
        for(i=0;i<=n+1;i++)arr[i].clear();
        dui.clear();
        getchar();
        for(i=1;i<=n+1;i++)fa[i]=i;
        for(i=0;i<n;i++)
        {
            gets(str);
            int len=strlen(str);
            for(j=0;j<len;j++)if(str[j]==‘ ‘)break;
            int pos=j;
            memset(tmp,0,sizeof(tmp));
            for(j=0;j<pos;j++)tmp[j]=str[j];
            string a=tmp;
            if(!dui[a])dui[a]=++cnt;
            int name1=dui[a],costs=0;
            while(str[pos]>‘9‘||str[pos]<‘0‘)pos++;
            for(j=pos;;j++)
            {
                if(str[j]==‘\0‘||str[j]>‘9‘||str[j]<‘0‘)
                {
                    pos=j;
                    break;
                }
                costs*=10;
                costs+=str[j]-‘0‘;
            }
            cost[name1]=costs;
            int nums=0;
            string b;
            for(j=pos+1;j<len;j++)
            {
                if(str[j]!=‘ ‘)
                {
                    tmp[nums++]=str[j];
                    if(str[j+1]==‘ ‘||str[j+1]==‘\0‘)
                    {
                        tmp[nums++]=‘\0‘;
                        b=tmp;
                        nums=0;
                        if(!dui[b])dui[b]=++cnt;
                        int name2=dui[b];
                        arr[name1].push_back(name2);
                        fa[name2]=name1;
                    }
                }
            }
        }
        for(i=1;i<=n+1;i++)
        {
            dp[i][0]=0;
            for(j=1;j<=m;j++)dp[i][j]=MAX;
        }
        for(i=1;i<=n;i++)if(fa[i]==i)arr[n+1].push_back(i);
        dfs1(n+1);
        dfs(n+1);
        printf("%d\n",dp[n+1][m]);
    }
}
/*
9 5
A 45 B C
B 20
C 30
D 60 E F
E 20
F 50 G H
G 10
H 25 I
I 15
*/
时间: 2024-08-04 20:55:45

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

【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;

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

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

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

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