〖题目〗
聚会的快乐
【问题描述】
你要组织一个由你公司的人参加的聚会。你希望聚会非常愉快,尽可能地多找些有趣的热闹。但劝你不要同时邀请某个人和他的上司,因为这可能带来争吵。给定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.