【时光回溯】【JZOJ3567】【GDKOI2014】石油储备计划

题目描述

这里写图片描述

输入

这里写图片描述

输出

对于每组数据,输出一个整数,表示达到“平衡”状态所需的最小代价。

样例输入

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

样例输出

4
4

数据范围

对于20%的数据,N<=15
对于100%的数据,T<=10,N<=100,0<=si<=10000,1<=X,Y<=N,1<=Z<=10000。

样例解释

对于第一组数据,从城市1到城市2运输2桶石油,代价为1*2=2;从城市3往城市2运输1桶石油,代价为2*1=2。此时三个城市储备量都为4桶,该状态的平衡度为0。
对于第二组数据,从城市2到城市5运输1桶石油,代价为1*4=4;此时五个城市储备量为(4,4,4,3,3),该状态的非平衡度为1.2,是能达到的所有状态的最小值。

解法

显然分配的桶数有(sum%n)个(sum/n+1)桶,其他都是sum/n个桶。
设f[i][j]为在i为根的子树中,放了j个(sum/n+1)桶。
这样对于这棵以i为根的子树,桶的总数是一定的,设为x;然后i与其父亲的流量显然是x-原桶数。
而一条边的贡献=流量*这条边的长度。
答案又是所有边的贡献之和。
所以给状态转移提供了条件。
没有必要把原树转化为二叉树。


题解还提供了网络流做法;以及二叉树形动态规划的做法。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define sqr(x) ((x)*(x))
#define ln(x,y) ll(log(x)/log(y))
using namespace std;
const char* fin="ex3567.in";
const char* fout="ex3567.out";
const ll inf=0x7fffffff;
const ll maxn=107,maxm=maxn*2;
ll t,n,m,m1,i,j,k,l,tot,INF;
ll fi[maxn],ne[maxm],la[maxm],a[maxn],va[maxm];
ll f[maxn][maxn],si[maxn],sum[maxn];
ll g[maxn][maxn][maxn];
void add_line(ll a,ll b,ll c){
    tot++;
    ne[tot]=fi[a];
    la[tot]=b;
    va[tot]=c;
    fi[a]=tot;
}
void dfs(ll v,ll from){
    ll i,j,k,l=0;
    g[v][l][0]=0;
    g[v][l][1]=0;
    si[v]=1;
    sum[v]=a[v];
    for (k=fi[v];k;k=ne[k]){
        if (la[k]!=from){
            dfs(la[k],v);
            si[v]+=si[la[k]];
            sum[v]+=sum[la[k]];
            for (i=0;i<=m;i++){
                if (f[la[k]][i]>=INF) continue;
                for (j=m-i;j>=0;j--){
                    if (g[v][l][j]>=INF) continue;
                    g[v][l+1][j+i]=min(g[v][l+1][j+i],g[v][l][j]+f[la[k]][i]+abs(sum[la[k]]-i*(m1+1)-(si[la[k]]-i)*m1)*va[k]);
                }
            }
            l++;
        }
    }
    for (i=0;i<=m;i++) f[v][i]=g[v][l][i];
}
int main(){
    scanf("%d",&t);
    for (;t;t--){
        scanf("%d",&n);
        memset(fi,0,sizeof(fi));
        memset(f,127,sizeof(f));
        memset(g,127,sizeof(g));
        INF=g[0][0][0];
        tot=0;
        k=0;
        for (i=1;i<=n;i++) scanf("%d",&a[i]),k+=a[i];
        m=k%n;
        m1=k/n;
        for (i=1;i<n;i++){
            scanf("%d%d%d",&j,&k,&l);
            add_line(j,k,l);
            add_line(k,j,l);
        }
        dfs(1,0);
        //f[1][m]=-f[1][m];
        printf("%lld\n",f[1][m]);
    }
    return 0;
}

启发

树形动态规划的必要条件之一:
设置的状态可以确定一棵子树所提供的贡献,从而可以进行转移。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值