POJ 1694 An Old Stone Game【递归+排序】

76 篇文章 0 订阅
3 篇文章 0 订阅

链接:



An Old Stone Game
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 3132 Accepted: 1422

Description

There is an old stone game, played on an arbitrary general tree T. The goal is to put one stone on the root of T observing the following rules: 
  1. At the beginning of the game, the player picks K stones and puts them all in one bucket. 
  2. At each step of the game, the player can pick one stone from the bucket and put it on any empty leaf. 
  3. When all of the r immediate children of a node p each has one stone, the player may remove all of these r stones, and put one of the stones on p. The other r - 1 stones are put back into the bucket, and can be used in the later steps of the game.

The player wins the game if by following the above rules, he succeeds to put one stone on the root of the tree. 
You are to write a program to determine the least number of stones to be picked at the beginning of the game (K), so that the player can win the game on the given input tree. 

Input

The input describes several trees. The first line of this file is M, the number of trees (1 <= M <= 10). Description of these M trees comes next in the file. Each tree has N < 200 nodes, labeled 1, 2, ... N, and each node can have any possible number of children. Root has label 1. Description of each tree starts with N in a separate line. The following N lines describe the children of all nodes in order of their labels. Each line starts with a number p (1 <= p <= N, the label of one of the nodes), r the number of the immediate children of p, and then the labels of these r children.

Output

One line for each input tree showing the minimum number of stones to be picked in step 1 above, in order to win the game on that input tree.

Sample Input

2
7
1 2 2 3
2 2 5 4
3 2 6 7
4 0
5 0
6 0
7 0
12
1 3 2 3 4
2 0
3 2 5 6
4 3 7 8 9
5 3 10 11 12
6 0
7 0
8 0
9 0
10 0
11 0
12 0

Sample Output

3
4

Source



题意:


        输入的第一行是测试数据组数 T 【T棵树】
      第二行表示当前树中有 N 个节点
      下面 N 行:
      每一行的前两个数 index num 分别表示当前节点的编号为 index, 有 num 个叶子
      剩下 num 个数表示各个叶子节点的编号

     
游戏规则:
      从叶子节点开始放石头,每个叶子放一个
      如果放满了当前节点的所有叶子节点, 那么可以拿掉所有的叶子节点的石头,
      并且从拿掉的石头中取出一个放在当前节点中
      :放到根节点 1 最少需要多少个石头


算法:递归+排序


思路:

来自 mmchen blog:  点击打开链接

         从叶子节点往根找,
       当前根的所有叶子节点所需石头数都找到
       先按照每个叶子节点所需的stone从大到小排序
       (1) 假设各个叶子节点所需石头
           a1 > a2 > a3...【中间没有 ==】 那么根节点所需最多stone就是 a1
       (2) 假设在排序后,各个叶子节点所需stone存在相等的情况
           a1 >= a2 >= a3 ...那么每次遇到一个 == ,stone 就要比 a1 多+1
           因为如果用 a1 个石头放满了第一个叶子后,还剩下 a1-1 个石头无法满足 a2
       (3) 排序后出现 a1 >= a2 > a3 > a4 >= a5 的交叉情况,
           根据填满前面的 叶子 后剩下的石头看是否满足 a 就好了,
           根据 (2) 的分析不满足最多只需 +1即可


code:

/********************************************************************************
E 	Accepted	336 KB	0 ms	C++	1059 B

看的 MMchen 的了Orz

题意:输入的第一行是测试数据组数 T 【T棵树】
      第二行表示当前树中有 N 个节点
      下面 N 行:
      每一行的前两个数 index num 分别表示当前节点的编号为 index, 有 num 个叶子
      剩下 num 个数表示各个叶子节点的编号
      游戏规则:
      从叶子节点开始放石头,每个叶子放一个
      如果放满了当前节点的所有叶子节点, 那么可以拿掉所有的叶子节点的石头,
      并且从拿掉的石头中取出一个放在当前节点中
      问:放到根节点 1 最少需要多少个石头

算法:递归+排序

思路: 从叶子节点往根找,
       当前根的所有叶子节点所需石头数都找到
       先按照每个叶子节点所需的stone从大到小排序
       (1) 假设各个叶子节点所需石头
           a1 > a2 > a3...【中间没有 ==】 那么根节点所需最多stone就是 a1
       (2) 假设在排序后,各个叶子节点所需stone存在相等的情况
           a1 >= a2 >= a3 ...那么每次遇到一个 == ,stone 就要比 a1 多+1
           因为如果用 a1 个石头放满了第一个叶子后,还剩下 a1-1 个石头无法满足 a2
       (3) 排序后出现 a1 >= a2 > a3 > a4 >= a5 的交叉情况,
           根据填满前面的 叶子 后剩下的石头看是否满足 a 就好了,
           根据 (2) 的分析不满足最多只需 +1即可

********************************************************************************/
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;

const int maxn = 210;

int node[maxn][maxn];

bool cmp(int a, int b)
{
    return a > b;
}

int solve(int index) //求解编号为 index 的节点所需石头
{
    if(node[index][0] == 0) //如果第index节点没有叶子,只需填满自己
    {
        return 1;
    }
    int tmp[maxn]; //暂存第 index 节点的每一个叶子所需的石头
    for(int i = 1; i <= node[index][0]; i++) // 遍历第 index 节点的每一个叶子
    {
        tmp[i] = solve(node[index][i]); //递归求解
    }
    sort(tmp+1, tmp+node[index][0]+1, cmp); //按所需石头从大到小排序
    int ans = tmp[1]; //排序后第一个最大
    for(int i = 2; i <= node[index][0]; i++) //遍历后面的看是否满足
    {
        if(tmp[i] > (ans-(i-1))) ans++;  //如果刚好填完前面 i-1 后不够,最多+1, 前面已经排序
    }
    return ans;

}

int main()
{
    int T;
    int n;
    scanf("%d", &T);
    while(T--)
    {
        memset(node, 0, sizeof(node));
        scanf("%d", &n); //总节点数
        int index, num;
        for(int i = 1; i <= n; i++)
        {
            scanf("%d%d", &index, &num); //index当前节点编号, num 当前节点拥有的叶子
            node[index][0] = num; // 0下标存叶子节点数目, 剩下的存当前根叶子编号
            for(int j = 1; j <= num; j++)
            {
                scanf("%d", &node[index][j]);
            }
        }
        int ans = solve(1); // 求解根节点
        printf("%d\n", ans);
    }
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 给出一个$n\times m$的矩阵,每个位置上有一个非负整数,代表这个位置的海拔高度。一开始时,有一个人站在其中一个位置上。这个人可以向上、下、左、右四个方向移动,但是只能移动到海拔高度比当前位置低或者相等的位置上。一次移动只能移动一个单位长度。定义一个位置为“山顶”,当且仅当从这个位置开始移动,可以一直走到海拔高度比它低的位置上。请问,这个矩阵中最多有多少个“山顶”? 输入格式 第一行两个整数,分别表示$n$和$m$。 接下来$n$行,每行$m$个整数,表示整个矩阵。 输出格式 输出一个整数,表示最多有多少个“山顶”。 样例输入 4 4 3 2 1 4 2 3 4 3 5 6 7 8 4 5 6 7 样例输出 5 算法1 (递归dp) $O(nm)$ 对于这道题,我们可以使用递归DP来解决,用$f(i,j)$表示以$(i,j)$为起点的路径最大长度,那么最后的答案就是所有$f(i,j)$中的最大值。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程中的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程中需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码 算法2 (动态规划) $O(nm)$ 动态规划的思路与递归DP类似,只不过转移方程和实现方式有所不同。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程中的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程中需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值