zoukankan      html  css  js  c++  java
  • P3354

    题面

    题意

    这道题的大致意思就是,在一个以0号节点为根的树上,我需要在k个结点修建伐木场。然后对于每一个点,它对答案的贡献是他木头产量乘上它离下游最近伐木场的距离。让你合理分配伐木场安置距离以使得贡献最小。

    分析

    其实很容易就会想到用(f_{i,j})来表示在i号点为根的子树上修建j个伐木场所得的最小贡献,但是我们发现状态并不好维护,所以我们需要改变一定的想法。

    我们这里采用(f_{i,j,k})表示在i为根的子树上修建k个伐木场,并且i下游的第一个伐木场在j,然后该状态下贡献的最小值。然后我们再记录(g_{i,j,k})为i号点上建造伐木场,并且在i的子树上一共有k个伐木场,i下游的第一个伐木场是j,然后该状态下的最小值。

    所以我们只要最后推到(f_{0,0,k})然后输出就是最后答案了。

    再考虑动态转移方程,很容易想到转移的过程就是一个背包的过程,然后为了记录某一结点所有的祖先,所以我们可以在dfs里加上手写栈(我比较懒,用了一个deque),这样只需要遍历栈就可以知道该点所有的祖先。

    代码

    简单背包模拟即可。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 105;
    
    struct edge
    {
    	int to, dist;
    };
    
    vector<vector<edge> > graph;
    
    int c[maxn], depth[maxn];
    long long f[maxn][maxn][maxn], g[maxn][maxn][maxn];
    deque<int> st;
    int n, k;
    
    void dfs(int p)
    {
    	st.push_back(p);
    	for (vector<edge>::iterator i = graph[p].begin(); i != graph[p].end(); i++)
    	{
    		depth[i->to] += depth[p] + i->dist;
    		dfs(i->to);
    		for (deque<int>::iterator j = st.begin(); j != st.end(); j++)
    			for (int l = k; ~l; l--)
    			{
    				f[p][*j][l] += f[i->to][*j][0];
    				g[p][*j][l] += f[i->to][p][0];
    				for (int x = 0; x <= l; x++)
    				{
    					f[p][*j][l] = min(f[p][*j][l], f[p][*j][l - x] + f[i->to][*j][x]);
    					g[p][*j][l] = min(g[p][*j][l], g[p][*j][l - x] + f[i->to][p][x]);
    				}
    			}
    	}
    	for (deque<int>::iterator j = st.begin(); j != st.end(); j++)
    	{
    		f[p][*j][0] += c[p] * (depth[p] - depth[*j]);
    		for (int l = 1; l <= k; l++)
    			f[p][*j][l] = min(f[p][*j][l] + c[p] * (depth[p] - depth[*j]), g[p][*j][l - 1]);
    	}
    	st.pop_back();
    	return;
    }
    
    int main()
    {
    	// freopen("riv.in", "r", stdin);
    	// freopen("riv.out", "w", stdout);
    	scanf("%d%d", &n, &k);
    	graph.resize(n + 1);
    	for (int i = 1; i <= n; i++)
    	{
    		int fa, dist;
    		scanf("%d%d%d", c + i, &fa, &dist);
    		graph[fa].push_back((edge){i, dist});
    	}
    	dfs(0);
    	printf("%d
    ", f[0][0][k]);
    	return 0;
    }
    
  • 相关阅读:
    Win32汇编对话框资源的综合应用
    linux下svn服务器搭建以及相关问题解决方案
    a+++b 在编译基础上的讨论
    BHO API HOOK Wininet基于IE编程的一些资料
    二维数组和二级指针
    深信服电话面试
    C和C++中的void*
    MySQL学习笔记:调用存储过程或函数报1418错误
    MySQL学习笔记:limit
    MySQL学习笔记:时间差
  • 原文地址:https://www.cnblogs.com/macesuted/p/solution-P3354.html
Copyright © 2011-2022 走看看