zoukankan      html  css  js  c++  java
  • bzoj4033: [HAOI2015]树上染色(树形dp)

    4033: [HAOI2015]树上染色

    Time Limit: 10 Sec  Memory Limit: 256 MB
    Submit: 3269  Solved: 1413
    [Submit][Status][Discuss]

    Description

    有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并
    将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。
    问收益最大值是多少。
     

    Input

    第一行两个整数N,K。
    接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to)。
    输入保证所有点之间是联通的。
    N<=2000,0<=K<=N
     

    Output

    输出一个正整数,表示收益的最大值。
     

    Sample Input

    5 2
    1 2 3
    1 5 1
    2 3 1
    2 4 2

    Sample Output

    17
    【样例解释】
    将点1,2染黑就能获得最大收益。

    HINT

     

    2017.9.12新加数据一组 By GXZlegend

    Source

    鸣谢bhiaibogf提供

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    #define N 2001
    
    using namespace std;
    int n,m,k,ans,cnt;
    int dis[N][N],e[N][N];
    int p[N];
    
    void calc_dis()
    {
        for(int k=1;k<=n;k++) for(int i=1;i<=n;i++)
        {
          for(int j=1;j<=n;j++)
              if(i!=j && j!=k) 
                e[i][j]=min(e[i][j],e[i][k]+e[k][j]);         
        }
    }
    
    void calc_ans()
    {
        int tmp=0;
        for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++)
        if(p[i]==p[j]) tmp+=e[i][j];
        ans=max(ans,tmp);
    }
    
    void dfs(int now,int c)
    {
        if(now==n+1)
        {
            if(c==k)calc_ans();
            return;
        }
        p[now]=0;dfs(now+1,c+1);
        p[now]=1;dfs(now+1,c);
    }
    
    int main()
    {
        int x,y,z;
        scanf("%d%d",&n,&k);
        memset(e,127/3,sizeof e);
        for(int i=1;i<=n;i++) e[i][i]=0;
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            e[x][y]=e[y][x]=z;
        }
        calc_dis();
        dfs(1,0);
        printf("%d
    ",ans);
        return 0;
    }
    30暴力
    /*
    开始状态设的是f[i][j][0/1]。 
    表示i为根的子树染j个黑点,i节点为黑/白的最大值。 
    发现当i与儿子异色时没法转移,需要知道子树内点的具体染色情况。
    所以说这个状态貌似不大行。
    
    网上说“像这种题目其实是一个套路。在树上这种“两两之间”计算贡献和的问题,都拆开看每条边的贡献。”
    我就很伤心,那不就说明我被套路了吗...
    
    改一下状态f[i][j]表示i为根染j个黑点对总答案的最大贡献。
    这个贡献的定义是啥呢?就是子树内所有边的贡献。 
    对于这条边
    子树下有j个黑点,那么在其他位置就有k - j个黑点,所以由于黑点这条边被走过j*(k-j)次。
    子树下有size[x] - j个白点,其他位置有n - k - (size[x]-j)个白点
    所以由于白点这条边被走过(size[x]-j)*(n-k-size[x]+j)次。
    
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    #define N 2001
    #define ll long long
    
    using namespace std;
    int n,k,ans,cnt,S,T;
    int head[N],siz[N];
    ll f[N][N],tmp;
    struct edge{
        int u,v,net;
        ll w;
    }e[N<<1];
    
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    inline void add(int u,int v,ll w)
    {
        e[++cnt].v=v;e[cnt].w=w;e[cnt].net=head[u];head[u]=cnt;
    }
    
    ll calc(ll val,int num,int x)//计算这条边的贡献 
    {
        val=val*x*(k-x)+val*(num-x)*(n-k-(num-x));
        return val;
    }
    
    void dfs(int u)
    {
        siz[u]=1;
        for(int i=head[u];i;i=e[i].net)
        {
            int v=e[i].v;
            if(siz[v]) continue;
            dfs(v);
            for(int x=siz[u];x>=0;x--) for(int y=siz[v];y>=0;y--)//类似树上背包,枚举当前子树染黑个数 
            {
                tmp=f[u][x]+f[v][y]+calc(e[i].w,siz[v],y);
                //f[u][x]其他子树内的点各自独立于当前子树内的点的贡献
                //f[v][y]当前子树内的点各自独立于其他子树内的点的贡献 
                f[u][x+y]=max(f[u][x+y],tmp);
            }
            siz[u]+=siz[v];
        }
    }
    
    int main()
    {
        int x,y,z;
        n=read();k=read();
        for(int i=1;i<n;i++)
        {
            x=read();y=read();cin>>z;
            add(x,y,z);add(y,x,z);
        }
        dfs(1);printf("%lld
    ",f[1][k]);
        return 0; 
    }
    折花枝,恨花枝,准拟花开人共卮,开时人去时。 怕相思,已相思,轮到相思没处辞,眉间露一丝。
  • 相关阅读:
    怎么对Navicat for Oracle 调试
    老版本的java代码与新代码如何找出差异
    Oracle 外部表是做什么用的
    如何在Navicat 中编辑和记录
    如何使用文件对比工具文件夹比较会话菜单
    哪些工具可以用来进行Bug管理
    5类开发者须知的工具
    怎么找出代码之间的差异
    Beyond Compare不仅可以修改网页代码
    文件对比工具有哪些用途
  • 原文地址:https://www.cnblogs.com/L-Memory/p/9768069.html
Copyright © 2011-2022 走看看