[Virtual Judge]SGU195:New Year Bonus Grant

来源:互联网 发布:葫芦侠软件源 编辑:程序博客网 时间:2024/04/30 08:59

点击打开题目链接

195. New Year Bonus Grant

time limit per test: 1.5 sec.
memory limit per test: 65536 KB
input: standard
output: standard



All programmers of Mocrosoft software company are organized in a strict subordination hierarchy. Every programmer has exactly one chief, except Bill Hates who is also the head of the company and has no chief. 

Due to the celebration of the new 2003 year, chief accountant of Mocrosoft decided to pay a New Year Bonus Grant of 1000 dollars to some programmers. However being extremely concerned of the company wealth she would like to designate the least possible amount of money for these grants. On the other hand she didn't want to be accused of being too greedy or of giving preferences to some programmers. To do this, she developed the following scheme of grants appointment: 


  • Each programmer may either assign a grant to one of his subordinates or have a grant assigned to him by his chief or none of the above. 
  • No programmer can simultaneously receive a grant and assign a grant to one of his subordinates. 
  • No programmer can assign a grant to more than one of his subordinates 


The scheme seemed to be designed perfectly — nobody would like to assign a grant to anybody since in this case he himself would not receive money. But programmers somehow discovered the plan of chief accountant and decided to make a trick to get the most money possible and share them fairly afterwards. The idea was to make such grant assignments that the total amount of grant money received is maximum possible. 

You were selected to write the program which will find the optimal grants appointment. 

Input

The first line of the input file contains integer N — the number of programmers in Mocrosoft company (2 ≤ N ≤ 500 000). Each programmer is assigned his unique identifier — integer number ranging from 1 to N. Bill Hates has number 1 and each programmer has the number greater then the number of his chief. The second line of the input file contains N-1 integers, i-th of which being the number of the chief of the worker whose number is (i + 1). 

Output

On the first line of the output file print the maximum possible amount of money workers can get. On the second line output the numbers of programmers that will receive grant in ascending order. 

Sample test(s)

Input
4 1 1 2 
Output
2000 
3 4 


=====================================题目大意=====================================


微软公司有着严谨的严格的隶属层次层次:每个员工(除了比尔盖茨以外)有且只有一个上司

新年的到来使得微软公司的总会计师决定给部分员工予以补助。

担心被指责过于贪婪或偏袒员工(因为他只给部分员工予以补助),总会计师研制了以下规则:


1、每个员工都可以指定给予他的下属补助和接受来自他上司的指定。

2、没有员工能同时指定给予他的下属补助和接受来自他上司的指定。

3、没有员工能指定给予他的多名下属补助。

注:这里的指定给予下属补助是说给予下属去公司领取一份补助的权利,并不是将自己拥有的补助给予下属。


方案看起来是完美的,因为为了保留“接受其上司补助”的权利,没有人会指定给予他的下属补助。

员工们不知何故发现了总会计师的计划,他们决定联合起来已得到最多的补助然后平分。

编程求解可以得到的最大总补助,并输出得到最大总补助的一种方案(即哪些员工得到了补助)。


=====================================算法分析=====================================


员工获得补助的限制条件(即规则2和规则3)综合即得:一个员工(用X表示)和他的所有下属中只能有一个人获得补助。因为:


1、若X获得了补助,必定是接受了其上司的指定,于是X不能再指定补助给予他的下属,此时获得补助的只有X。

2、若X的一名下属获得了补助,必定是接受了X的指定,于是X既不能接受其上司的指定也不能再指定其他下属,此时获得补助的只有

    一名下属。


所以题目可抽象为:在一棵有根数上(在微软公司员工隶属关系树上),

                 选取尽可能多的节点(选择尽可能多的员工),

                 满足每个节点和他的子节点中(满足每个员工和他的所有下属中),

                 只有一个节点被选取(只有一个人获得补助)。


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


一、树形DP

   

    1、状态转移:

    

    用F[i][0]表示不选第i个节点,F[i][1]表示选取第i个节点,则:

    F[i][0]=∑F[son of i][0]-MIN(F[the son of i][0]-F[the son of i][1])。     

    F[i][1]=∑F[son of i][0]+1。


    2、深度搜索:


    由于每个节点的父节点唯一且在深度搜索过程中父节点仅会调用每个子节点的数据一次,因此深度搜索不需要增添记忆化搜索语句。


    3、方案输出(最为麻烦):


    a.在深度搜索过程中记录在不选取当前节点cur时选取的子节点son(令BestSon[cur]=son)。

    b.初始化数组ChooseHash为false。

    c.遍历所有节点,若当前节点cur的父节点未被选取且其为父节点的最佳子节点时,标记cur会被选取(令ChooseHash[cur]=true)。

    d.扫描数组ChooseHash,输出值为true的节点编号。


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


二、贪心


    首先考虑:应不应该选取最底层的节点?(下图中A为最底层的节点,B为A的父节点,红色标记已被选取的节点,灰色标记不可选

              取的节点)

   

    分类讨论:1、不选取B子树的任何节点,则B子树中选取的节点数为0,此时不对B子树外任何节点的选取造成影响。

              2、选取B子树的叶节点(以A为例),则B子树中选取的节点数为1,此时不对B子树外的任何节点的选取造成影响(图

                 中左树所示)。

              3、选取B子树的根节点(即B节点),则B子树中选取的节点数为1,此时会使B子树外的B节点的兄弟节点和父亲节点无

                 法被选取(图中右树所示)。

    比较可知:第三种选择为最佳选择,即最底层的叶节点必须被选取。


    根据题意:员工的编号大于他上司的编号(原文在Input中:each programmer has the number greater then the number of 

              his chief)

    于是又有:最底层的节点即编号最大的节点。

    那么便可:1、选取父节点以及自身未被标记的节点中编号最大的节点:

                 通过父节点是否被标记可知兄弟节点是否被选取,通过自身是否被标记可知子节点是否被选取。

              2、标记选取节点的父节点。

              3、重复上述步骤直到没有节点可供选取为止。


=======================================代码=======================================

一、树形DP




#include<stdio.h>#include<string.h>const int MAXN=500005;const int INF4=0x1f1f1f1f;int N,F[MAXN][2],SubordListHead[MAXN],BestSon[MAXN],MemTot;bool ChooseHash[MAXN];struct Node { int ID,Next; } NodeMem[MAXN];void TreeDP(int CurID){int sum=0,min=INF4;for(int son=SubordListHead[CurID];son!=-1;son=NodeMem[son].Next){int sonid=NodeMem[son].ID;TreeDP(sonid);                       //计算子节点数据 sum+=F[sonid][0];                    //sum=∑F[son of CurID][0]if(F[sonid][0]-F[sonid][1]<min){BestSon[CurID]=sonid;            //更新最佳子节点min=F[sonid][0]-F[sonid][1];     //min=MIN(F[son of CurID][0]-F[son of CurID][1])}} F[CurID][0]=(min==INF4?0:sum-min);       //F[i][0]=∑F[son of CurID][0]-MIN(F[son of CurID][0]-F[son of CurID][1])F[CurID][1]=sum+1;                       //F[i][1]=∑F[son of CurID][0]+1}void GetBestScheme(int CurID,bool CurIsChoose){ChooseHash[CurID]=CurIsChoose;for(int son=SubordListHead[CurID];son!=-1;son=NodeMem[son].Next){//只有未被选择的父节点的最佳子节点才会被选择bool SonNeedChoose=(!CurIsChoose&&NodeMem[son].ID==BestSon[CurID]);GetBestScheme(NodeMem[son].ID,SonNeedChoose);}}void OutBestScheme(){memset(ChooseHash,0,sizeof(ChooseHash));TreeDP(1);GetBestScheme(1,false);printf("%d\n",F[1][0]*1000);for(int i=2;F[1][0];++i)                {if(ChooseHash[i]){printf("%d%c",i,--F[1][0]?' ':'\n');}}}void BuildTree(){MemTot=0;memset(SubordListHead,-1,sizeof(SubordListHead));for(int i=1;i<=N-1;++i){int ChiefID;scanf("%d",&ChiefID);NodeMem[MemTot].ID=i+1;              //将节点接在链表头部NodeMem[MemTot].Next=SubordListHead[ChiefID];SubordListHead[ChiefID]=MemTot++;}}int main(){while(scanf("%d",&N)==1){BuildTree();OutBestScheme();}return 0;}

二、贪心




#include<stdio.h>  #include<string.h>int N,Father[500005],Ans[500005];bool Unelectable[500005];  int main()  {  while(scanf("%d",&N)==1){for(int i=2;i<=N;++i){scanf("%d",&Father[i]);}memset(Unelectable,0,sizeof(Unelectable));int sum=0;for(int i=N;i>=2;--i){if(!Unelectable[i]&&!Unelectable[Father[i]]){Unelectable[Father[i]]=1;Ans[sum++]=i;}}printf("%d\n",sum*1000);for(int i=sum-1;i>=0;--i) {printf("%d%c",Ans[i],i?' ':'\n');}    }      return 0;  }

原创粉丝点击