zoukankan      html  css  js  c++  java
  • 树形dp-道路[HNOI2018]

    题面真的好长(.jpg)

    所以考场上某初二大佬打了85分的题我(TM)一分都没拿(QAQ)

    (算了,我在讲什么?)

    首先是把罪恶的题面化简:

    给一棵树,1节点为根,叶子节点均为农村,城市都是非叶节点,每个城市都有两条边,一条为铁路,一条为公路,现在你可以对每个非叶节点 选择其中一条 进行番修。

    番修后假设一个叶子节点到根要经过(x)条未被番修的道路,(y)条未被番修的公路,那么其对答案产生的贡献为:((c_i * (a_i+x) * (b_i+y)))

    要求最小贡献。

    这道题出在省选上,记得当时看的时候,以为它和毒瘤((Day1T3))一样毒瘤,而且题面太长了,就没写了.......

    然而实际上个人觉得没有机房那些大佬说的那么水啊 QAQ

    观察数据,我们发现其实可以记录每个点到根节点

    首先考虑记录状态:(dp_{x,i,j})表示以(x)根的子树,上面有(i)条公路未被番修,(j)条铁路未被番修的答案。

    如果(dp)到了最底下的叶子节点,我们可以(O(K^2))的去算答案。

    当然,要记录每个点顶上有多少条道路和有多少条公路

    对于非叶节点,转移也并不难想:

    
    for( int i = 0; i <= dep1[x]; ++ i )
    for( int j = 0; j <= dep2[x]; ++ j ) 
    dp[x][i][j] = dp[ls(x)][i][j] + dp[rs(x)][i][j + 1], 
    //这里表示修左儿子的公路,那么右儿子顶上就有一条铁路未被番修。
    
    dp[x][i][j] = min( dp[x][i][j], dp[ls(x)][i + 1][j] + dp[rs(x)][i][j] );
    
    //此处类似
    

    然而这样会 (MLE)

    我们可以发现,某次(dp)(dp)到某一层后,其在回溯去更新的点,只有它的父亲,换而言只:某个点的(dp)值只有在更新其父亲的时候才生效,然后其(dp)不会影响其他点

    如果我们将原来的过程写成(dfs),我们可以发现,某次(dp)的过程中,对于某一种/层深度,我们只会用到两个点。(左儿子+右儿子)

    所以我们可以类似滚动数组去做,用深度去更新,在更新完后此状态无用。

    最后的代码在这里啦:

    
    #include<bits/stdc++.h>
    using namespace std;
    int read() {
    	char cc = getchar(); int cn = 0, flus = 1;
    	while(cc < '0' || cc > '9') {  if( cc == '-' ) flus = -flus;  cc = getchar();  }
    	while(cc >= '0' && cc <= '9')  cn = cn * 10 + cc - '0', cc = getchar();
    	return cn * flus;
    }
    #define int long long
    const int N = 40000 + 5;
    const int K = 45;
    const int M = 105;
    #define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
    #define ls(x) son[x][0]
    #define rs(x) son[x][1]
    int dp[M][K][K], son[N][3], fa[N], dep1[N], dep2[N]; 
    int head[N], cnt, n, a[N], b[N], c[N];
    void dfs( int x, int dep, int k ) {
    	if( !son[x][0] ) {
    		rep( i, 0, dep1[x] ) rep( j, 0, dep2[x] ) 
    		dp[dep + k * dep][i][j] = c[x] * ( a[x] + i ) * ( b[x] + j );
    		return ;
    	}
    	dep1[ls(x)] = dep1[x] + 1, dep2[ls(x)] = dep2[x]; //dep1表示公路 
    	dep1[rs(x)] = dep1[x], dep2[rs(x)] = dep2[x] + 1; 
    	dfs(ls(x), dep + 1, 0 ), dfs(rs(x), dep + 1, 1 );
    	rep( i, 0, dep1[x] ) rep( j, 0, dep2[x] )
    	dp[dep + k * dep][i][j] = min( dp[dep + 1][i][j] + dp[2 * (dep + 1)][i][j + 1], dp[dep + 1][i + 1][j] + dp[2 * (dep + 1)][i][j] );
    }
    signed main()
    {
    	n = read();
    	rep( i, 1, n - 1 ) {
    		son[i][0] = read(), son[i][1] = read();
    		if( son[i][0] < 0 ) son[i][0] = -son[i][0] + n;
    		if( son[i][1] < 0 ) son[i][1] = -son[i][1] + n;
    	}
    	rep( i, 1, n ) a[i + n] = read(), b[i + n] = read(), c[i + n] = read();
    	dfs( 1, 0, 0 );
    	printf("%lld
    ", dp[0][0][0] );
    	return 0;
    }
    
    
  • 相关阅读:
    PAT甲级1056Mice and Rice
    李宏毅机器学习课程笔记-5.3神经网络中的反向传播算法
    李宏毅机器学习课程笔记-5.2神经网络为什么要是深度的
    PAT甲级1014Waiting in Line
    python批量处理邮件:poplib和email快速上手教程
    李宏毅机器学习课程笔记-5.1深度学习之引言
    李宏毅机器学习课程笔记-4.2分类模型之概率生成模型
    李宏毅机器学习课程笔记-4.1分类简介及其与回归的区别
    类自动加载封装类
    var与let循环中经典问题
  • 原文地址:https://www.cnblogs.com/Soulist/p/10629281.html
Copyright © 2011-2022 走看看