题目来源:

HihoCoder1199

题目要求:
    There is a tower defense game with n levels(missions). The n levels form a tree whose root is level 1.

In the i-th level, you have to spend pi units of money buying towers and after the level, you can sell the towers so that you have qi units of money back.

Each level is played once. You can choose any order to complete all the levels, but the order should follow the following rules:

1: A level can be played when all its ancestors are completed.

2: The levels which form a subtree of the original tree should be completed consecutively in your order.

You want to know in which order of completion you can bring the minimum money before starting the first level.

题目大意:
    游戏包含N个关卡,这些关卡的关系可以用一个树形结构来描述,进入每个关卡i需要金钱pi,完成该关卡后可以获得回报qi。游戏中可以任意选择关卡来完成,但需要满足如下两个条件:
    (1) 对于每一个关卡,只有它的父节点代表的关卡已经完成,该关卡才可以进行。
    (2) 如果有多个关卡组成了一棵子树,则这些关卡必须以连续的顺序完成(即如果遍历到某一节点,那么接下来就必须遍历以它为根的子树的所有节点。
    题目要求通过合理地安排关卡通过的顺序,计算出进入整个游戏前,需要携带的最少数目的钱数。

解答:
    
本题中关卡完成的过程其实就是树的遍历过程,由于子节点访问前要求父节点已经完成访问,因此,这里的遍历过程是前序遍历。但遍历根节点后,对于子树的遍历顺序却不确定。这里不同的子树遍历顺序就会得到不同的“初始资金”(进入游戏前需要携带的钱数)的结果。本题的关键在于如何找到使得初始资金达到最小值的子树遍历顺序。

·初始资金的计算:
    对于游戏中的“初始资金”,可以采用下面的方式计算。对于给定的一个关卡的序列:s1, s2, ... sn,如果一次遍历序列中的每一个关卡,则我们可以用下面的过程来计算从s1遍历至si时需要的所有花费:首先遍历s1,需要资金p1,当关卡s1完成后,可以得到q1的资金;此时遍历s2,需要p2资金,而此时已有q1的资金,因此还需要p2-q1的资金,关卡s2完成后,可以得到q2资金,然后遍历s3.....以此类推,遍历到si后,就拥有qi-1的资金,此时还需要补充pi - qi-1资金。因此如果用Wi表示从关卡s1遍历至关卡si时,在不包括关卡si的回报时,所需要投入的总的资金数,那么:

这里的计算结果Wi,是完成关卡s1到si实际所需要的资金数目。此时,我们可以讨论一下对于不同的i值,Wi的大小关系。对于Wi,我们总可以将Wi写成下面的形式:

 

显然,如果pi>qi-1,Wi就大于Wi-1,否则Wi就比Wi-1小。出现这种现象的原因是:每一个关卡的实际消耗资金可能会比其初始投入资金要少,因此,完成关卡时,投入的资金可能要比关卡实际消耗的资金要多,因此对于Wi比Wi-1小的情况:如果初始资金为Wi,遍历关卡s1到si-1的过程中一定存在“赊欠”的情况,即在完成某个关卡前就已经取用了当前关卡完成后的回报资金。
    对于序列:s1, s2, .... sn,我们可以取i= 1, 2, ... n,就可以得到N个不同的Wi值,而完成序列中所有的关卡所需要的初始投入资金,就是这些Wi值的最大值,即:

 

对于有N个关卡的序列,共有N!个不同的排列方式,如果将这些排列分别记作:a1, a2, ...aN!,那么题目中的答案就是这N!种排列中计算的初始投入资金的最小值,如果将其记作W,那么:

 

接下来的问题是如何确定到底哪个排列结果才会得到最小的W值。

·只有两个子树的情况:
    首先我们考虑一种简单的情况,即:对于每个节点,它至多有两个子树,这里可以将它们分别称为左子树和右子树。由于在遍历子树时必须先遍历根节点,所以,这里共有两种遍历方式:根->左->右,或者:根->右->左。下面分别考虑这个两种情况,记:根节点、左子树节点、右子树节点分别为节点1、2、3,如下图:

    对于:根->左->右 的遍历顺序,我们可以计算经过每个节点时,所需要的最小的资金数,如下:

对于:根->右->左 的顺序也可以采用同样的处理方式:

此时,最小的初始金额为:

     

这里,我们可以这样的现象:L1=R1, L3≥R2 并且 R3≥L2。然后,分两种情况考虑:
    ① q3 > q2: 此时:R3 < L3。由于 Max{L1,L2,L3}≥L3,因此:Max{L1, L2, L3}≥L3;又因为:L3≥R2,因此:Max{L1, L2, L3}≥R2;又:Max{L1, L2, L3}≥L1 = R1,所以:Max{L1, L2, L3}≥R1,因此,我们可以得到这样的结论:

此时:

② q2 > q3: 此时,和情况①类似,我们可以得到下面的结论:

对于以上两种情况,我们可以发现:产生最终结果的遍历顺序一定是先访问q值大的子树,然后再访问q值小的子树,因此我们可以得到结论: 
    对于只有两棵子树的情况,子树的遍历以q值递减的顺序时,可以得到最优解。

·更一般的情况:
    下面将前文中的结论推广到更一般的情况。前面已经说明,对于有N个节点的一个排列:S1, S2, ... Sn,它的初始投入资金计算方式为:

     

这里我们从序列中找到这样的两个相邻的元素Si和Si+1使得:qi<qi+1, 并且将上面式子中的每一项都展开,如下:

然后,我们将si和si+1的位置交换,上面的式子就变为:

    

通过观察上面的两个式子,可以发现这样的特点,对于W和W'的计算式子中的每一项Wk,可以发现:
    ① 1≤k≤i-1时:Wk = W'k
    ② k=i 或 k=i+1时:有:Wk+1≥W'k 并且 W'k+1≥Wk;又因为:qi+1>qi,所以还有:Wk+1>W'k+1。
    ③ k>i+1时:Wk = W'k
于是,我们可以得到:

     

于是,我们发现:通过交换Si和Si+1,最初的投入资金就会减少。所以,下面的结论是成立的:
    对于序列S1, S2, ..., Sn,如果存在两个相邻的元素:Si和Si+1满足:qi<qi+1,那么交换Si和Si+1的位置,可以使得最初的投入金额减少。

接下来,对于任意的序列S1, S2, ... Sn,我们通过反复查看S1和S2, S2和S3.....Sn-1和Sn这些相邻的元素的q值的大小,如果qi<qi+1那么就将Si和Si+1交换,知道整个序列不再发生变化。
    可以发现,上面的步骤是对原序列以q值为关键字进行冒泡排序,因此,当序列不在发生变化时,对于任意的操作序列,其结果都是唯一的,即以q值递减排列的序列,由于每一次的交换操作都会使得最初投入金额减少,因此,操作完成后,最终的序列对应的初始投入金额一定是最少的。
    所以,我们得到了下面的结论:
      对于序列S1, S2, ..., Sn,如果按照q值递减的次序来遍历,那么其初始投入金额最少。 

·子树的p值和q值:
    程序的输入只是给出了每个节点的p值和q值,而对于整个子树的p值和q值,则需要我们在程序中计算。这里的计算逻辑很简单,对于子树的p值,就是遍历子树时的最小初始突入金额,而q值则是初始投入金额减去整个子树的实际消耗费用后的剩余部分。
    所以解答本题的基本思路是,首先遍历根节点,然后递归地遍历它的子树,遍历完成后,每个子树的p值和q值都为已知,然后以q值递减的次序计算当前子树的初始投入金额。最后整棵树的p值就是本题的答案。

以上是本题的解答思路。

备注:
    需要特别注意的是,题目中的输入仅仅是表示了那两个节点有边,但哪个点是父节点,哪个节点是子节点在输入中是无法体现的。笔者曾因这个问题WA了很久。。。。T-T

输入输出格式:
    输入:

    The first line contains one single integers, n - the number of levels. 

The next n lines describes the levels. Each line contains 2 integers, pi and qi which are described in the statement.

The next n-1 lines describes the tree. Each line contains 2 integers, ai and bi which means there is an edge between level ai and level bi.

    输出:
             Print one line with an single integer representing the minimum cost.

数据范围:

     1 <= n <= 10000
     0 <= qi <= pi <= 20000
     For 30% of the data, n<=100.
     For 60% of the data, n<=1000.
  
程序代码:

/****************************************************/
/* File        : hiho_week_109                      */
/* Author      : Zhang Yufei                        */
/* Date        : 2016-07-30                         */
/* Description : HihoCoder ACM program. (submit:g++)*/
/****************************************************/#include<stdio.h>
#include<stdlib.h>/** Define the edge of the graph.* Parameters:*     @dst: The destination of the edge.*        @next: The next field.*/
typedef struct node1 {int dst;struct node1 *next;
} edge;/* Define the node of tree.* Parameters:*        @p: The cost to buy the tower.*        @q: The benefit after finishing the tower.*        @p_tree: The cost to buy the tree.*        @q_tree: The benefit after finishing the tree.*        @edges: All the edge of the node.*     @visited: Mark if the tower has been visited. */
typedef struct node2 {int p;int q;int p_tree;int q_tree;edge *edges;int visited;
} node; // Record all the levels.
node *levels;// Input data, the number of levels.
int n;// The tmp space used in compute function.
node **tmp;/** The comparison function used in quick sort.* Parameters:*        @p1 & p2: The 2 objects to compare.* Returns:*     If p1 < p2, returns 1, or if p1 > p2, returns -1,*        or returns 0.*/
int cmp(const void* p1, const void* p2) {node *n1 = *((node**) p1);node *n2 = *((node**) p2);if(n1->q_tree < n2->q_tree) {return 1;} else if(n1->q_tree == n2->q_tree) {return 0;} else {return -1;}
}/** This function computes the initial cost and the terminal benefit* of the tree.* Parameters:*       @root: The root of tree.* Returns:*        None.*/
void compute(node* root) {root->visited = 1;edge *cur = root->edges;int sum = root->p - root->q;while(cur != NULL) {node *des = &levels[cur->dst];if(des->visited == 0) {compute(des);sum += des->p_tree - des->q_tree;}cur = cur->next;}int count = 0; cur = root->edges;while(cur != NULL) {tmp[count++] = &levels[cur->dst];cur = cur->next;}if(count > 0) {qsort(tmp, count, sizeof(node*), cmp);root->p_tree = root->p;int height = root->p - root->q;for(int i = 0; i < count; i++) {height += tmp[i]->p_tree;if(root->p_tree < height) {root->p_tree = height;}height -= tmp[i]->q_tree;}root->q_tree = root->p_tree - sum;} else {root->p_tree = root->p;root->q_tree = root->q;}
} /** The main program.*/
int main(void) {scanf("%d", &n);levels = (node*) malloc(sizeof(node) * n);tmp = (node**) malloc(sizeof(node*) * n);for(int i = 0; i < n; i++) {scanf("%d %d", &levels[i].p, &levels[i].q);levels[i].p_tree = levels[i].q_tree = 0;    levels[i].edges = NULL;levels[i].visited = 0;}    for(int i = 0; i < n - 1; i++) {int a, b;scanf("%d %d", &a, &b);a--;b--;edge *e1 = (edge*) malloc(sizeof(edge));e1->dst = b;e1->next = levels[a].edges;levels[a].edges = e1;edge *e2 = (edge*) malloc(sizeof(edge));e2->dst = a;e2->next = levels[b].edges;levels[b].edges = e2;}compute(&levels[0]);printf("%d\n", levels[0].p_tree);return 0;
}

ACM解题总结——HihoCoder1199 (微软笔试题)相关推荐

  1. 数位DP--由一道微软笔试题引起

    前天晚上,一位研三的学长突然跑到我们宿舍,问我们一道微软笔试题.给你一个整数n,求出1到n这个区间范围内包含数字0的个数,例如当n=10的时候就只有10包含0,输出1,n=90就输出9.唯一的要求是此 ...

  2. ios笔试题算法_微软笔试题-Dijkstra算法

    Dijkstra算法是典型的算法.Dijkstra算法是很有代表性的算法.Dijkstra一般的表述通常有两种方式,一种用永久和临时标号方式,一种是用OPEN, CLOSE表的方式,这里均采用永久和临 ...

  3. 微软笔试题,机器人消砖块

    我比较傻叉,居然忘了用动态规划做,用了递归,各种边界判断,而且数组稍大一点就栈溢出.递归可以剪支,稍微减少一些递归次数.不管怎么样还是贴上自己的傻叉代码吧 #include<iostream&g ...

  4. 微软笔试题 2013暑期实习笔试题目

    2019独角兽企业重金招聘Python工程师标准>>> 1: 有1000瓶水,其中1瓶是有毒的,小老鼠如果喝了有毒的水会在一个星期后死掉,问至少需要多少只小老鼠来做实验,才能够在一星 ...

  5. 微软笔试题(看到的写答案啊)

    int func(int n, int* r) { printf("n=%d *r=%d\n",n,*r);//加了个打印的东西 return n && (*r + ...

  6. 亚洲研究院微软笔试题

    http://hi.baidu.com/houqingxin/blog/item/0a7b3b1e2068f9f71ad57655.html

  7. 一道有趣的微软笔试题

    老师d 的物理测验答案在教室里丢失了,今天那个教室上了5 堂课,老师d 上了3 堂,有可 能是a.b.c 三个同学盗窃 已知:1.a 上了两堂课 2.b 上了三堂课 3.c 上了四堂课 4.a.b.c ...

  8. 【转】IT名企面试:腾讯笔试题(2)

    摘要:想要进入腾讯公司,面试笔试题是一定要有所准备的.那么这里我们总结了一些腾讯笔试题,例如:const的含义及实现机制等问题. 腾讯是国内数一数二的IT企业了.那么每年想要进入腾讯公司的应聘者也是络 ...

  9. 90 个名企笔试题和算法题

    名企笔试 名企笔试:美团2016招聘笔试(股票交易日) 名企笔试:搜狐2016招聘笔试题(扎金花) 名企笔试:凤凰网2015招聘(二分查找) 名企笔试:4399游戏校招笔试题(快速排序) 名企笔试:蘑 ...

  10. 搬:90 个名企笔试题和算法题

    名企笔试 名企笔试:美团2016招聘笔试(股票交易日) 名企笔试:搜狐2016招聘笔试题(扎金花) 名企笔试:凤凰网2015招聘(二分查找) 名企笔试:4399游戏校招笔试题(快速排序) 名企笔试:蘑 ...

最新文章

  1. 【Android 应用开发】Activity 返回堆栈清除操作 ( 默认状态 | 清除返回堆栈配置 | 不清除返回堆栈配置 | 清除指定界面配置 )
  2. html里面执行js跳转页面,JS函数执行和指定跳转其他页面
  3. java有什么字符串_Java 中操作字符串都有哪些类?它们之间有什么区别
  4. boost::core模块default_allocator
  5. Eclipse 导出
  6. 如何当好独立CIO(1)
  7. ConstantSourceNode
  8. 送给python新手关于pip用法和虚拟环境用法的介绍(英文)
  9. 系统测试总结报告模板
  10. 月球 地球 三维位置_微软的月球飞行模拟器是一次了不起的未完成的地球之旅...
  11. 前端安全须知(淘宝)
  12. Android OTA升级原理和流程分析(五)---update.zip包从上层进入Recovery服务
  13. 驾培行业应对新形势“自学直考”新格局冲击剖析
  14. Windows8系统不能玩传奇的解习办法
  15. java计算机毕业设计三门峡市旅游景点一站式服务规划系统演示录像源程序+mysql+系统+lw文档+远程调试
  16. 计算机素养以应用工具为核心,计算机素养的培养.doc
  17. 【无代码体验】使用鲸智搭开发《招聘管理系统》
  18. 医学图像处理和深度学习入门
  19. ublox lea-6r
  20. 华为1+X网络系统建设与运维(中级)——IPv6协议概述

热门文章

  1. 【Oracle】执行计划详解
  2. python科赫雪花小包裹_034 实例8-科赫雪花小包裹-Go语言中文社区
  3. 基于FPGA的VGA显示,简单的历程和注释(DE2-115)
  4. 缠中说禅电子书_缠中说禅股技术理论(完整版).pdf
  5. 关于子网的网络地址和广播地址的计算问题的方法
  6. bt采集蜘蛛,开源版
  7. 解析大型门户网站教给我们的PV增加技巧
  8. 自动阅读专业版第九次更新---原薅羊毛专业版(最后一次源代码分享)
  9. axios封装之cancelToken
  10. 资源---vc++2010学习版---注册码(注册码密匙)