zoukankan      html  css  js  c++  java
  • 【JZOJ4814】【NOIP2016提高A组五校联考2】tree

    题目描述

    给一棵n 个结点的有根树,结点由1 到n 标号,根结点的标号为1。每个结点上有一个物品,第i 个结点上的物品价值为vi。
    你需要从所有结点中选出若干个结点,使得对于任意一个被选中的结点,其到根的路径上所有的点都被选中,并且选中结点的个数不能超过给定的上限lim。在此前提下,你需要最大化选中结点上物品的价值之和。
    求这个最大的价值之和。

    输入

    第一行为两个整数n; lim
    接下来n 行,第i 行包含一个整数vi,表示结点i 上物品的价值。
    接下来n- 1 行,每行包含两个整数u; v, 描述一条连接u; v 结点的树边。

    输出

    输出一行答案。

    样例输入

    6 4
    -5
    4
    -6
    6
    9
    6
    3 2
    3 1
    2 4
    2 5
    1 6

    样例输出

    2

    数据范围

    对于前20% 的数据,1<=n; lim<=10
    对于前60% 的数据,1<=n; lim<=100
    对于100% 的数据,1<=n; lim<=3000; |vi| <=10^5 数据有梯度,保证给出的是合法的树。

    解法

    把条件转化,设原树有根,如果一个结点不选,那么以其为根的子树都不能选。
    显然我们可以把树转化成dfs序列。
    设f[i][j]为前i个数中,选了j个数的最大答案。
    f[i][j]=max{f[i][j],f[i1][j1],f[low[i]][j1]}
    时间复杂度为O(n2)

    代码

    #include<iostream>
    #include<stdio.h>
    #include<math.h>
    #include<string.h>
    #include<algorithm>
    #define ll long long
    #define ln(x,y) int(log(x)/log(y))
    #define sqr(x) ((x)*(x))
    using namespace std;
    const char* fin="tree.in";
    const char* fout="tree.out";
    const int inf=0x7fffffff;
    const int maxn=3007,maxm=maxn*2,maxa=100000;
    int n,m,i,j,k,tot,ans=0;
    int a[maxn],fi[maxn],ne[maxm],la[maxm];
    int f[maxn][maxn],de[maxn];
    int c[maxn],low[maxn],dfn[maxn];
    void add_line(int a,int b){
        tot++;
        ne[tot]=fi[a];
        la[tot]=b;
        fi[a]=tot;
    }
    void dfs(int v,int from){
        int i,j,k;
        c[++c[0]]=v;
        dfn[v]=c[0];
        for (k=fi[v];k;k=ne[k])
            if (la[k]!=from) {
                dfs(la[k],v);
            }
        low[dfn[v]]=c[0];
    }
    int main(){
        freopen(fin,"r",stdin);
        freopen(fout,"w",stdout);
        scanf("%d%d",&n,&m);
        memset(f,128,sizeof(f));
        for (i=1;i<=n;i++) scanf("%d",&a[i]);
        for (i=1;i<n;i++){
            scanf("%d%d",&j,&k);
            add_line(j,k);
            add_line(k,j);
        }
        dfs(1,0);
        f[0][0]=0;
        for (i=0;i<n;i++){
            for (j=0;j<m;j++){
                if (f[i][j]<-2000000000) continue;
                f[i+1][j+1]=max(f[i+1][j+1],f[i][j]+a[c[i+1]]);
                f[low[i+1]][j]=max(f[low[i+1]][j],f[i][j]);
            }
        }
        for (i=1;i<=n;i++) for (j=0;j<=m;j++) ans=max(ans,f[i][j]);
        printf("%d",ans);
        return 0;
    }

    启发

    尝试把条件以另一个角度看待,那么新的方法就会出现。
    当要处理子树问题时,不妨把原树转化为dfs序列。

  • 相关阅读:
    WebService
    JavaMail
    ssh框架整合
    CSS3初步
    SpringMVC 文件上传及下载
    Java多线程
    SpringMVC 数据校验
    初始化参数绑定——日期格式
    SpringMVC入门
    Quartz
  • 原文地址:https://www.cnblogs.com/hiweibolu/p/6714875.html
Copyright © 2011-2022 走看看