洛谷P2014 选课

来源:互联网 发布:好吃的月饼 知乎 编辑:程序博客网 时间:2024/06/16 00:40

题目来源:https://www.luogu.org/problem/show?pid=2014#sub


树形dp。关键是存树的方式。


在森林上找多个包含树根的连通块,使所有点的权值最大。


用二叉树存储,定义两个数组head和next,其中head[i]表示节点i的第一个儿子节点,next[i]表示节点i的兄弟节点。


若以知一个节点的父亲,则插入该节点的代码:


if(head[fa]==0)head[fa]=i;else{       int t=head[fa];       while(next[t]!=0)t=next[t];       next[t]=i;}

dp方程:


f[c][s]表示在二叉树中以c为根的子树中取s个节点的最大权值,其中这s个节点均联通。


f[c][s]=a[c]+max(f[head[c]][i],f[next[c]][s-1-i]);


由于二叉树中节点c的右节点实际上与c是兄弟关系,故f[c][s]的值可以不包含节点c,故还应保证


f[c][s]=max(f[c][s],f[next[c]][s]);


代码:


#include <algorithm>#include <iostream>#include <cstring>#include <cstdlib>#include <sstream>#include <cstdio>#include <string>#include <vector>#include <cmath>#include <ctime>#include <stack>#include <queue>#include <set>#include <map>using namespace std;int a[1001];int head[1001],next[1001];int f[1001][1001];int dp(int c,int s){    if(s==0)return 0;    if(c==0)return 0;    if(f[c][s]!=0)return f[c][s];    for(int i=0;i<s;i++)f[c][s]=max(f[c][s],a[c]+dp(head[c],i)+dp(next[c],s-1-i));    f[c][s]=max(f[c][s],dp(next[c],s));    return f[c][s];}int main(){    ios::sync_with_stdio(false);    int n,m;cin>>n>>m;    for(int i=1;i<=n;i++)    {        int fa;cin>>fa;        if(head[fa]==0)head[fa]=i;        else        {            int t=head[fa];            while(next[t]!=0)t=next[t];            next[t]=i;        }        cin>>a[i];    }    cout<<dp(head[0],m);    return 0;}


0 0
原创粉丝点击