• JZOJ 3567. 【GDKOI2014】石油储备计划


    题目

    解析

    多种解法:有上下界费用流(nb),树形DP等
    而由于我太菜,前者待日后再补
    下面介绍树形DP的解法

    首先我们等发现一些性质:
    最后使方差最小,树的每个点权值必然在 ([sum/n..sum/n+1]) 之间,其中 (sum) 指石油总和
    那么我们可不可以试试枚举最后有多少个点为 (sum/n+1)
    当然可以

    (f_{i,j}) 表示以 (i) 为根的子树有 (j) 个点权值为 (sum/n+1)
    那么转移时枚举它的儿子,它子树的 (j),它儿子子树的 (j)
    (f_{i,j}=f_{son,k}+f_{i,j-k}+c*|s[v] - (ave + 1)*k - ave*(sz[v] - k)|)
    (c) 为子树到 (i) 的边权,(s_v) 表示子树的油的总和,那么后面一截就是必须流出去或流进来的,经过 (c) 这条边。
    注意我们枚举时是先用某一个儿子来更新原来的 (f),而转移时又要借助 (father) 子树的 (j),所以我们不能直接修改,而是应该在所有 (j) 都枚举完后再修改,具体可见代码

    总共是 (O(n^3))

    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<iostream>
    using namespace std;
    typedef long long LL;
    
    const int N = 105;
    int n , num , sz[N] , h[N] , tot;
    LL f[N][N] , g[N] , s[N] , ave;
    
    struct edge{
    	int to , w , nxt;
    }e[N << 1];
    
    inline void add(int x , int y , int z){e[++tot] = edge{y , z , h[x]} , h[x] = tot;}
    inline void dfs(int x , int fa)
    {
    	sz[x] = 1;
    	f[x][0] = f[x][1] = 0;
    	for(register int i = h[x]; i; i = e[i].nxt)
    	{
    		int v = e[i].to;
    		if (v == fa) continue;
    		dfs(v , x) , s[x] += s[v] , sz[x] += sz[v];
    	}
    	for(register int i = h[x]; i; i = e[i].nxt)
    	{
    		int v = e[i].to;
    		if (v == fa) continue;
    		memset(g , 0x3f3f3f3f , sizeof g);
    		for(register int j = 0; j <= num && j <= sz[x]; j++)
    			for(register int k = 0; k <= j && k <= sz[v]; k++)
    				g[j] = min(g[j] , f[v][k] + f[x][j - k] + (LL)abs(s[v] - (ave + 1)*k - ave*(sz[v] - k)) * e[i].w);
    		for(register int j = 0; j <= num && j <= sz[x]; j++) f[x][j] = g[j];
    	}
    }
    
    int main()
    {
    	int T;
    	scanf("%d" , &T);
    	for(; T; T--)
    	{
    		scanf("%d" , &n);
    		ave = 0;
    		for(register int i = 1; i <= n; i++) scanf("%lld" , &s[i]) , ave += s[i];
    		num = ave % n , ave = ave / n , tot = 0 , memset(h , 0 , sizeof h);
    		int x , y , z;
    		for(register int i = 1; i < n; i++) 
    		{
    			scanf("%d%d%d" , &x , &y , &z);
    			add(x , y , z) , add(y , x , z);
    		}
    		memset(f , 0x3f3f3f3f , sizeof f);
    		dfs(1 , 0);
    		printf("%lld
    " , f[1][num]);
    	}
    }
    
  • 相关阅读:
    docker 部署springboot
    CentOS 7 安装docker
    008自瞄原理
    007根据矩阵基地址绘制方框
    006寻找矩阵
    005分析其他人基地址
    易语言读取鼠标坐标x,y
    003获取鼠标x,y
    Oracle单机Rman笔记[0]---环境准备
    系统优化设计笔记--曹大公众号文章笔记
  • 原文地址:https://www.cnblogs.com/leiyuanze/p/13498231.html
Copyright © 2020-2023  润新知