[经典题目]聚会的快乐

〖题目〗

聚会的快乐

【问题描述】

    你要组织一个由你公司的人参加的聚会。你希望聚会非常愉快,尽可能地多找些有趣的热闹。但劝你不要同时邀请某个人和他的上司,因为这可能带来争吵。给定N个人(姓名,他的幽默系数,以及他上司的名字),编程找到能是幽默系数和最大的若干个人。

【输入格式】party.in

    第一行一个整数N(N<100)。接下来每一行描述一个人的信息,信息之间用空格隔开。姓名的长度不超过20的字符串,幽默系数是在0到100之间的整数。

【输出格式】party.out

    所邀请的人最大的幽默系数和

【输入样例】

5

BART 10 HOMER

HOMER 20 MONTGOMERY

MONTGOMERY 10 NOBODY

LISA 30 HOMER

SMITHERS 40 MONTGOMERY

【输出样例】

80

〖分析〗

很遗憾,本来应该1次AC的题目竟然交了2次。看来,读懂题目还是非常必要的。“失之毫厘,差之千里”啊!

最初的理解,把“劝你不要同时邀请某个人和他的上司”理解成如果一个人被邀请了,那么他的上司以及上司的上司等都不能被邀请。但样例实在太BT了,这样算出来答案竟然是对的!

然后就兴高采烈地提交了。然后WA……

其实这道题目应该也是树状DP。不过我还是用记忆化搜索了——在解决图的DP问题上,个人更倾向使用记忆化搜索。先是处理,把人名换成编号——这个用PAS实在很累,如果用CPP就爽快多了。这个过程我想不用我详细解释了。NOIP普及组1=的也应该会了(参见下面过程INIT->CL)。

然后就是DP过程。对于树状DP,一般是把森林或者树转化为二叉树,然后进行DP。对于这道题目,构成的图是一个森林。只要新建了个结点0,然后这个结点与森林里的每棵树的树根相连。这样,一个森林被转化成一棵树。接下来是公式:

              sum(f(k))            (树k是树root的子树)

f(root)=max

             sum(f(kk))+a[root]     (树kk是树root的子树的子树)

其中,f(root:tree)表示树root内可以得到的最大的幽默系数和。a[root]表示树root的根结点的幽默系数。

利用这个公式,把f(root)放入递归计算(也可以从叶递推到根)。最后的答案就是f(root)。

至此,《聚会的快乐》正式解决。

〖程序〗

// TASK: party

type

  people=record

           na,nla:string;

           ha,la:longint;

         end;

var

  n:longint;

  f:text;

  a:array [0..100] of people;

 procedure init;

 var

   i,j,k,kk:longint;

   str:string;

  procedure cl;

  var

    i,j,k:longint;

    ls:string;

    len:longint;

  begin

    ls:='';

    len:=length(str);

    i:=1;

    while (i<=len)and(str[i]<>' ') do

    begin

      ls:=ls+str[i];

      inc(i);

    end;

    k:=1;

    while (k<=kk)and(a[k].na<>ls) do inc(k);

    if k>kk then

    begin

      inc(kk);

      a[kk].na:=ls;

      k:=kk;

    end;

    inc(i);ls:='';

    while (i<=len)and(str[i]<>' ') do

    begin

      ls:=ls+str[i];

      inc(i);

    end;

    val(ls,a[k].ha,j);

    inc(i);ls:='';

    while (i<=len)and(str[i]<>' ') do

    begin

      ls:=ls+str[i];

      inc(i);

    end;

    a[k].nla:=ls;

  end;

 begin

   assign(f,'party.in');reset(f);

   fillchar(a,sizeof(a),0);

   readln(f,n);kk:=0;

   for i:=1 to n do

   begin

     readln(f,str);

     cl;

   end;

   for i:=1 to n do

     for j:=1 to n do

       if a[i].nla=a[j].na then

       begin

         a[i].la:=j;

         break;

       end;

   a[0].ha:=0;

   close(f);

 end;

 procedure ouot(ans:longint);

 var

   i,j,k:longint;

 begin

   assign(f,'party.out');rewrite(f);

   writeln(f,ans);

   close(f);

 end;

 function calc(dex:longint):longint;

 var

   i,j,k:longint;

   c1,c2:longint;

 begin

   c1:=0;

   for i:=1 to n do

     if a[i].la=dex then

       inc(c1,calc(i));

 

   c2:=a[dex].ha;

   for i:=1 to n do

     if a[i].la=dex then

       for j:=1 to n do

         if a[j].la=i then

         inc(c2,calc(j));

 

   if c1>c2 then exit(c1)

   else exit(c2);

 end;

begin

  init;

  ouot(calc(0));

end.

转载于:https://www.cnblogs.com/klarkxy/archive/2009/07/21/10017207.html

你可能感兴趣的:([经典题目]聚会的快乐)