http://poj.org/problem?id=1909

Marbles on a tree
Time Limit: 1000MS Memory Limit: 30000K
Total Submissions: 1473 Accepted: 735

Description

n boxes are placed on the vertices of a rooted tree, which are numbered from 1 to n, 1 <= n <= 10000. Each box is either empty or contains a number of marbles; the total number of marbles is n. 
The task is to move the marbles such that each box contains exactly one marble. This is to be accomplished be a sequence of moves; each move consists of moving one marble to a box at an adjacent vertex. What is the minimum number of moves required to achieve the goal? 

Input

The input contains a number of cases. Each case starts with the number n followed by n lines. Each line contains at least three numbers which are: v the number of a vertex, followed by the number of marbles originally placed at vertex v followed by a number d which is the number of children of v, followed by d numbers giving the identities of the children of v. 
The input is terminated by a case where n = 0 and this case should not be processed.

Output

For each case in the input, output the smallest number of moves of marbles resulting in one marble at each vertex of the tree.

Sample Input

9
1 2 3 2 3 4
2 1 0
3 0 2 5 6
4 1 3 7 8 9
5 3 0
6 0 0
7 0 0
8 2 0
9 0 0
9
1 0 3 2 3 4
2 0 0
3 0 2 5 6
4 9 3 7 8 9
5 0 0
6 0 0
7 0 0
8 0 0
9 0 0
9
1 0 3 2 3 4
2 9 0
3 0 2 5 6
4 0 3 7 8 9
5 0 0
6 0 0
7 0 0
8 0 0
9 0 0
0

Sample Output

7
14
20
题目中要求每次移动只能是将一个子弹移动到相邻的顶点。就是说每个顶点只能把多余的子弹移动到父亲,少了要从父亲拿。所以一个子树如果子弹正好就不用向祖先借子弹。用自底向上的方法,求出每个顶点多的或少的子弹数的和,就是结果了。
代码:
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 10000+50;
int a[maxn];
int tree[maxn];
vector<int>g[maxn];
int n;
int ans = 0;

void init() {
  for (int i = 1; i <= n; i ++) {
    g[i].clear();
    tree[i] = -1;
  }
  ans = 0;
}

void dfs(int root) {
    int x;
    for (int i = 0; i < (int)g[root].size(); i ++) {
       x = g[root][i];
       dfs(x);
       if (a[x] <= 0) {
         a[root] += a[x]-1;
         ans -= a[x]-1;
       } else {
         a[root] += a[x]-1;
         ans += a[x]-1;
       }
    }
}
int main() {
    while (scanf("%d", &n), n) {
        init();
        int x, y, root;
        for (int i = 1; i <= n; i ++) {
            scanf("%d", &root);
            scanf("%d", &a[root]);
            scanf("%d", &x);
            for (int j = 1; j <= x; j ++) {
                scanf("%d", &y);
                g[root].push_back(y);
                tree[y] = root;
            }
        }
        for (int i = 1; i <= n; i ++) {
            if (tree[i] == -1) {
                dfs(i);
            }
        }
        printf("%d\n", ans);
    }
return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值