zoukankan      html  css  js  c++  java
  • 【刷题】【树形dp】

    1>树上染色

    (题解复制自luogu)

    题目要求将k个点染成黑色,求黑点两两距离及白点两两距离,使他们之和最大。

    我们可以将距离转化为路径,然后再将路径路径拆分成边,就可以记录每条边被经过的次数,直接计算即可。

    很简单对吧?那么问题来了,距离转化为路径好理解,路径拆为边也好说,可是每条边被经过的次数怎么计算呢?

    我们可以这样想,我们任意取两个同色的点,对于每一条边,若不在这两个点的路径上,我们自然不考虑,若是在两个点的路径上,那么这条边的计数加一。我们可以转换一下,若是两个点在边的一侧,则不影响计数,若在边的两侧,则边的计数加一。那么我们推广一下,便可以得出,一条边的两侧每有一对同色点,这条边就要被经过一次。也就是说,一条边被经过的次数等于边的两侧同色点个数的乘积。那么我们便可以求出每条边被经过的次数

    tot=k*(m-k)+(sz[v]-k)*(n-m-sz[v]+k)tot=k(mk)+(sz[v]k)(nmsz[v]+k)

    mm表示题目要求选的黑点数,sz[v]sz[v]表示当前子节点的子树大小,kk表示当前子节点的子树上已选择的黑点数

    有了这个结论,我们就可以轻松地得出DP方程了。

    f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]+tot*e[i].w)f[u][j]=max(f[u][j],f[u][jk]+f[v][k]+tote[i].w)

    就是关于这个方程让我在做题的时候纠结了好久,为什么kk正序排列就是对的,倒序排列就是错的?已有的题解也没有做出很好的解释,我A了之后也没有继续研究。多亏了帮同学找树形DP入门题时我重新注意到了这道题,使我对这一奇怪的现象产生了疑惑。得到了DDOSvoid大佬的帮助并进行了多次试验后,我终于明白了其中的原因,也让我对这道题的理解加深了数层。

    这道题kk前几篇题解必须正序枚举的原因并不是什么要用j-kjk更新答案,而是因为正序枚举kk是从00开始的,而这道题的状态转移必须要先将k=0k=0的状态转移过来才能成立。也就是说,这只是个巧合,jj的枚举要倒序没错,但kk的枚举必须正序简直就是无稽之谈。要想避免这一情况,只需提前转移一下k=0k=0的情况即可。

    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<cstring>
    #define ll long long
    using namespace std;
    ll n,k;
    const int N=2003;
    struct node
    {
        int v,w;
        node(int vv,ll ww)
        { v=vv,w=ww; }
        node(){}
    };
    vector <node> g[N];
    
    ll sz[N];
    ll f[N][N];
    void dfs(int rt,int fa)
    {
        sz[rt]=1,f[rt][0]=f[rt][1]=0;
        //不合法的状态的值可能很大,所以还是memset 
        int s=g[rt].size();
        for(int i=0;i<s;i++)
            if(g[rt][i].v !=fa) 
                dfs(g[rt][i].v ,rt),sz[rt]+=sz[g[rt][i].v ]; 
        
        for(ll i=0;i<s;i++)
        {
            node nx=g[rt][i];
            if(nx.v ==fa) continue;
            
        for(ll j=min(sz[rt],k);j>=0;j--)//下面只是因为好写才倒序 //不是,不这么写,就会状态重复计算
                for(ll l=0;l<=sz[nx.v ] && l<=j;l++)
                {
                    if(f[rt][j-l]==-1) continue;
                    
                    ll val=nx.w *l*(k-l) +nx.w *(sz[nx.v ]-l)*(n-k-sz[nx.v ]+l);
                    f[rt][j]=max(f[rt][j],f[rt][j-l]+f[nx.v ][l]+val);
                }
        }
    }
    
    int main()
    {
        scanf("%d%d",&n,&k);
        k=min(k,n-k);
        int u,v;ll w;
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%lld",&u,&v,&w);
            g[u].push_back(node(v,w)) ;
            g[v].push_back(node(u,w));
        }
        
        memset(f,-1,sizeof(f));
        dfs(1,-1);
        printf("%lld
    ",f[1][k]);
        
        return 0; 
    }

    2>数字转换

     

  • 相关阅读:
    MarkDown学习
    做网站的或者博客的网站
    Calendar日历简单用法
    Cron表达式范例
    CSVFileUtil 读取写入CSV文件简单工具类
    直接读取ZIP包数据 线上、线下方法
    Spring @Async/@Transactional 失效的原因及解决方案
    多线程学习 读写锁
    多线程学习 公平锁和非公平锁
    多线程学习 ThreadLocal的使用。
  • 原文地址:https://www.cnblogs.com/xwww666666/p/11647104.html
Copyright © 2011-2022 走看看