ZOJ - 3231 Apple Transportation 树形dp

Apple Transportation

  ZOJ - 3231


There's a big apple tree in the forest. In the tree there are N nodes (numbered from 0 to N - 1), and the nodes are connected by branches. On each node of the tree, there is a squirrel. In the autumn, some apples will grow on the nodes. After all apples are ripe, each squirrel will collect all the apples of their own node and store them. For the demand to be harmonic, they decide to redistribute the apples to minimize the variance (please refer to the hint) of apples in all nodes. Obviously, an apple cannot be divided into several parts. To reach this goal, some transportation should be taken. The cost of transporting x apples from node u to node v is x * distance (node u, node v). Now given the current amount of apples of each node and the structure of the apple tree, you should help the squirrels to find the minimal cost to redistribute the apples.

Input

Input consists of multiple test cases (less than 80 cases)!

For each test case, the first line contains an integer N (1 <= N <= 100), which is the number of the nodes in the tree.

The following line contains N integers a0,a1,...,aN-1 (0 <= ai <= 10000), representing the amount of the i-th node's apples.

The following N - 1 lines each contain three integers uvc (0 <= u,v <= N - 1, 0 <= c <= 1000), which means node u and node v are connected by a branch, the length of the branch is c.

There is a blank line between consecutive cases.

Output

For each case output the minimal total transportation cost. The minimal cost is guaranteed to be less than 231.

Sample Input

3
1 2 3
0 1 1
0 2 1

3
1 3 3
0 1 3
0 2 4

2
1 2
0 1 1

Sample Output

1
3
0

Hint

The formula to calculate the variance  of x1x2, ..., xn:


Source

ZOJ - 3231


My Solution

题意:有一颗n节点的苹果树,每个节点上有一定数量的苹果ai,树上有一些松鼠,他们想让苹果平均分配,即每个节点的方差最小。它们可以任意地将苹果在节点之间移动,

每移动一个苹果所花费的代价是移动的路程。求最小的总代价。


树形dp

方差最小其实就是等价于 有 sigma{ai} mod n个节点上有 sigma{ai} / n + 1个苹果,其他都有 sigma{ai} / n 个。

f[i][j]表示在节点i代表的子树中有j个节点上有 sigma{ai} / n + 1个苹果,其他的为sigma{ai} / n 时的最小代价。

f[u][j] = min{sigma{f[v][numt] + abs(sum[v] - cnt[v] * average - numt)*diat[u][v]]}}

其中numt表示这个子节点v代表的子树有 numt个节点上有 sigma{ai} / n + 1个苹果,有cnt[v]个节点,sum[v]个苹果,

需要枚举numt,且要求保证 sigma{numt} == j..

最终答案是 f[0][res].

复杂度 小于 O(n^3)


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 1e2 + 8;

int head[MAXN], tot;
struct esge{
    int u, v, w, nxt;
} a[2*MAXN];

inline void add(int u, int v, int w)
{
    tot++;
    a[tot].u = u, a[tot].v = v, a[tot].w = w;
    a[tot].nxt = head[u], head[u] = tot;
}

LL f[MAXN][MAXN], tmp[MAXN];
int val[MAXN], cnt[MAXN], sum[MAXN], ave, res;

inline void dfs(int u, int fa)
{
    f[u][0] = f[u][1] = 0;
    cnt[u] = 1; sum[u] = val[u];
    int i, j, k, v;
    for(k = head[u]; k; k = a[k].nxt){
        if(a[k].v == fa) continue;
        v = a[k].v;
        dfs(v, u);
        memset(tmp, 127, sizeof tmp);
        for(i = cnt[u] - 1; i >= 0; i--){
            for(j = cnt[v]; j >= 0; j--){
                if(cnt[u] == 1){
                    tmp[i+j] = f[v][j] + abs(sum[v] - cnt[v]*ave - j)*a[k].w;
                }
                else{
                    tmp[i+j] = min(tmp[i+j], f[u][i] + f[v][j] + abs(sum[v] - cnt[v]*ave - j)*a[k].w);
                }
            }
        }
        cnt[u] += cnt[v];
        sum[u] += sum[v];
        for(i = 0; i <= cnt[u]; i++){
            f[u][i] = tmp[i];
        }
    }
    for(i = cnt[u]; i >= 1; i--) f[u][i] = min(f[u][i], f[u][i-1]); //考虑子树的根节点有 average + 1 个苹果的情况
}


int main()
{
    #ifdef LOCAL
    freopen("1.in", "r", stdin);
    //freopen("1.out", "w", stdout);

    #endif // LOCAL
    ios::sync_with_stdio(false); cin.tie(0);

    int n, u, v, w, i, s;
    while(cin >> n){
        memset(f, 127, sizeof f);
        memset(a, 0, sizeof a);
        memset(head, 0, sizeof head);
        s = 0; tot = 1;
        for(i = 0; i < n; i++){
            cin >> val[i];
            s += val[i];
        }
        for(i = 1; i < n; i++){
            cin >> u >> v >> w;
            add(u, v,  w);
            add(v, u, w);
        }
        ave = s / n;
        res = s % n;
        dfs(0, -1);
        cout << f[0][res] << "\n";
    }
    return 0;
}


  Thank you!

                                                                                                                                             ------from ProLights

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值