【NOI2005】 小H的聚会
题目描述
小 H 的聚会 【任务描述】 小 H 从小就非常喜欢计算机,上了中学以后,他更是迷上了计算机编程。经 过多年的不懈努力,小 H 幸运的被选入信息学竞赛省队,就要去他日思夜想的 河南郑州参加第 22 届全国信息学奥林匹克竞赛(NOI2005) 。 小 H 的好朋友小 Y 和小 Z 得知了这个消息, 都由衷的为他感到高兴。 他们准 备举办一个 party,邀请小 H 和他的所有朋友参加,为小 H 庆祝一下。 经过好几天的调查, Y 和小 Z 列出了一个小 H 所有好友的名单, 小 上面一共 有 N 个人(方便起见,我们将他们编号为 1 至 N 的整数) 。然而名单上的人实在 是太多了,而且其中不少人小 Y 和小 Z 并不认识。如何把他们都组织起来参加 聚会呢? 小 Y 和小 Z 希望为小 H 的 N 个好友设计一张联系的网络,这样,若某个人 得知了关于聚会的最新情况, 则其他人都可以直接或间接得到消息。同时为了尽 量的保证消息传递得简单、高效以及最重要的一点:保密(为了给小 H 一个惊 喜,在 party 的筹备阶段这个聚会的消息是绝对不能让他知道的) ,小 Y 和小 Z决定让尽量少的好友直接联系: 为了保证 N 个好友都能互相直接或间接联系到,只需要让(N-1)对好友直接联系就可以了。 图 1 显然,名单上的好友也不都互相认识,而即使是两个互相认识的人,他们之 间的熟悉程度也是有区别的。因此小 Y 和小 Z 又根据调查的结果,列出了一个 好友间的关系表, 表中标明了哪些人是可以直接联系的,而对于每一对可以互相 联系的好友,小 Y 和小 Z 又为他们标出了联系的愉快程度。如 3 和 4 的关系非 常好,因此标记他们之间的联系愉快程度为 10;而 1 和 3 是一般的朋友,则他 们的愉快程度要小一些。上面的图 1 表示一个 N=5 的联系表,其中点表示名单 上的好友,边则表示两个好友可以直接联系,边上的数字即为他们联系的愉快程 度。 小 Y 和小 Z 希望大家都能喜欢这次聚会, 因此决定在尽量最大化联系网络的 愉快程度:所谓联系网络的愉快程度,即每一对直接联系人之间的愉快程度之 和。如在图 1 中,加粗的边表示了一个让愉快程度最大联系的网络,其愉快程度 为 5+6+10+5=26。 然而,如果让某个人直接和很多的人联系,这势必会给他增添很大的负担。 因此小 Y 和小 Z 还为每个人分别设定了一个最大的直接联系人数 ki,表示在联 系网络中,最多只能有 ki 个人和 i 直接联系。 还是用图 1 的例子,若我们为 1 至 5 每个点分别加上了 ki = 1, 1, 4, 2, 2 的限 制,则上述方案就不能满足要求了。此时的最优方案如图 2 所示,其愉快程度为 3+6+10+5 = 24。 图 2 你能帮小 Y 和小 Z 求出在满足限制条件的前提下, 愉快程度尽量大的一个联 系网络吗? 【输入格式】 输入文件 party1.in 到 party10.in 已经放在用户目录中。 每个输入文件的第 1 行都是两个整数 N 和 M。N 表示小 H 的好友总数,M 表 示小 Y 和小 Z 列出来的可以直接联系的好友对数。 输入文件的第 2 行包含 N 个在[1, N-1]范围内的整数,依次描述 k1, k2, ..., kN。 相邻的两个数字之间用一个空格隔开。 以下 M 行,每行描述一对可以互相联系的好友,格式为 ui vi ci。表示 ui 和 vi 可以直接联系,他们的联系愉快程度为 ci。 另外,在所有这些数据的最后还有单独的一行包括一个(0, 1]范围内的实数 d 作为评分系数。 你的程序并不需要去理会这个参数, 但你可以根据这个参数的提示去设计不同的算法。有关 d 的说明,可以参见后面的评分方法。 【输出格式】 本题是一道提交答案式的题目,你需要提供十个输出文件从 party1.out 到 party10.out。 每个文件的第 1 行为一个整数,表示你找到的最大的愉快程度。 以下(N-1)行,描述这个网络。每行一个数 ei,表示在网络中,让输入文件中 第(ei + 2)行描述的一对好友直接联系。 【输入样例】 56 11422 125 133 236 253 3 4 10 455 0.00001 【输出样例】 24 2 3 5 6 【样例说明】 详见任务描述中的例子。 【评分方法】 本题设有部分分,对于每一个测试点: Ø 如果你的输出方案不合法,即 ei 不符合范围或 ei 有重复或网络不连通等, 该测试点得 0 分。 Ø 如果你输出的方案和输出文件第 1 行的愉快程度不一致,该测试点得 0分。 Ø 否则该测试点得分按如下方法计算:设 a = (1 − d ) * our _ ans b = (1 + d * 0.5) * our _ ans u 如果你的结果小于 a,该测试点得 0 分; u 如果你的结果大于 b,该测试点得 15 分; u 否则你的得分为 your _ ans − a your _ score = ------------------------ *10 our _ ans − a 其中的 d 为评分系数(输入数据中最后一行的实数) ,our_ans 为我们提供的 参考解答,your_ans 为你的答案。 【你如何测试自己的输出】 我们提供 party_check 这个工具作为测试你的输出文件的办法。 使用这个工具 的方法是在控制台中输入: ./party_check <测试点编号 X> 在你调用这个程序后, party_check 将根据输入文件 partyX.in 和你的输出文件 partyX.out 给出测试的结果,其中包括: l Error: Not connected:你的程序输出的联系网络不连通; l Error: Edge xxx is duplicated:第 xxx 条边被输出了两次; l Error: Edge in Line xxx is out of range:你的程序在第 xxx 行输出的边的编 号不在[1, M]范围内; l Error: Degree of Friend xxx is out of range:在联系网络中,和编号为 xxx 的好友直接联系的人超过了限制; l Error: Scheme & happiness mismatch:方案和第一行的愉快程度不一致; l 测试程序非法退出:其他情况; Correct! Happiness = xxx:输出正确。
最大生成树(kruskal)
1 (* 2 *Problem: NOI2005 小 H 的聚会 3 *Author : Chen Yang 4 *Time : 2012.6.5 10:00 am 5 *State : Solved 6 *Memo : 最大生成树(kruskal) 7 *) 8 program party; 9 type 10 ty=record 11 x,y,d,num:longint; 12 end; 13 14 var 15 n,m,i,x,y,z,ans:longint; 16 a:array[0..100000] of ty; 17 father,d:array[0..100000] of longint; 18 get:array[0..100000] of boolean; 19 //============== 20 procedure sort(l,r:longint); 21 var 22 i,j,m:longint; 23 t:ty; 24 begin 25 i:=l; j:=r; m:=a[(i+j)>>1].d; 26 repeat 27 while a[i].d>m do inc(i); 28 while a[j].d<m do dec(j); 29 if i<=j then 30 begin 31 t:=a[i]; a[i]:=a[j]; a[j]:=t; 32 inc(i); dec(j); 33 end; 34 until i>j; 35 if i<r then sort(i,r); 36 if j>l then sort(l,j); 37 end; 38 //============== 39 function getfather(x:longint):longint; 40 begin 41 if x=father[x] then exit(x); 42 father[x]:=getfather(father[x]); 43 exit(father[x]); 44 end; 45 //============== 46 procedure hebin(x,y:longint); 47 var 48 i,j:longint; 49 begin 50 i:=getfather(x); j:=getfather(y); 51 father[i]:=j; 52 end; 53 //============== 54 begin 55 assign(input,'party6.in'); reset(input); 56 assign(output,'party6.out'); rewrite(output); 57 read(n,m); 58 for i:=1 to n do read(d[i]); 59 for i:=1 to n do father[i]:=i; 60 for i:=1 to m do 61 begin 62 read(x,y,z); 63 a[i].x:=x; a[i].y:=y; a[i].d:=z; a[i].num:=i; 64 end; 65 sort(1,m); 66 for i:=1 to m do 67 begin 68 if (getfather(a[i].x)<>getfather(a[i].y))and(d[a[i].x]>0)and(d[a[i].y]>0) then 69 begin 70 inc(ans,a[i].d); 71 get[a[i].num]:=true; 72 hebin(a[i].x,a[i].y); 73 dec(d[a[i].x]); dec(d[a[i].y]); 74 end; 75 end; 76 writeln(ans); 77 for i:=1 to m do 78 if get[i] then writeln(i); 79 close(input); close(output); 80 end.
随机化贪心(爬山法)
1 (* 2 *Problem: NOI2005 小 H 的聚会 3 *Author : Chen Yang 4 *Time : 2012.6.5 10:00 am 5 *State : Solved 6 *Memo : 随机化贪心(爬山法) 7 *) 8 program party; 9 const xx='8'; 10 maxt=100; 11 mmt=100000; 12 type 13 ty=record 14 x,y,d,num:longint; 15 end; 16 17 var 18 n,m,i,t,x,y,z,ans,now,last,time:longint; 19 tmp:ty; 20 a:array[0..100000] of ty; 21 father,d,dx:array[0..100000] of longint; 22 get:array[0..100000] of boolean; 23 //============== 24 procedure sort(l,r:longint); 25 var 26 i,j,m:longint; 27 t:ty; 28 begin 29 i:=l; j:=r; m:=a[(i+j)>>1].d; 30 repeat 31 while a[i].d>m do inc(i); 32 while a[j].d<m do dec(j); 33 if i<=j then 34 begin 35 t:=a[i]; a[i]:=a[j]; a[j]:=t; 36 inc(i); dec(j); 37 end; 38 until i>j; 39 if i<r then sort(i,r); 40 if j>l then sort(l,j); 41 end; 42 //============== 43 function getfather(x:longint):longint; 44 begin 45 if x=father[x] then exit(x); 46 father[x]:=getfather(father[x]); 47 exit(father[x]); 48 end; 49 //============== 50 procedure hebin(x,y:longint); 51 var 52 i,j:longint; 53 begin 54 i:=getfather(x); j:=getfather(y); 55 father[i]:=j; 56 end; 57 //============== 58 function calc:longint; 59 var 60 tot,num:longint; 61 begin 62 for i:=1 to n do father[i]:=i; 63 fillchar(get,sizeof(get),0); 64 dx:=d; tot:=0; num:=0; 65 for i:=1 to m do 66 begin 67 if (getfather(a[i].x)<>getfather(a[i].y))and(dx[a[i].x]>0)and(dx[a[i].y]>0) 68 then 69 begin 70 inc(tot,a[i].d); inc(num); 71 get[a[i].num]:=true; 72 hebin(a[i].x,a[i].y); 73 dec(dx[a[i].x]); dec(dx[a[i].y]); 74 end; 75 end; 76 if num=n-1 then exit(tot) else exit(-maxlongint); 77 end; 78 //============== 79 begin 80 randomize; 81 assign(input,'party'+xx+'.in'); reset(input); 82 read(n,m); 83 for i:=1 to n do read(d[i]); 84 for i:=1 to m do 85 begin 86 read(x,y,z); 87 a[i].x:=x; a[i].y:=y; a[i].d:=z; a[i].num:=i; 88 end; 89 close(input); 90 assign(input,'party'+xx+'.out'); reset(input); 91 read(ans); 92 close(input); 93 for t:=1 to maxt do 94 begin 95 sort(1,m); time:=0; 96 while time<mmt do 97 begin 98 repeat 99 x:=random(m)+1; y:=random(m)+1; 100 until x<>y; 101 tmp:=a[x]; a[x]:=a[y]; a[y]:=tmp; 102 now:=calc; 103 if now>last then 104 begin 105 last:=now; time:=0; 106 if now>ans then 107 begin 108 assign(output,'party'+xx+'.out'); rewrite(output); 109 ans:=now; 110 writeln(ans); 111 for i:=1 to m do 112 if get[i] then writeln(i); 113 close(output); 114 end; 115 end else 116 begin 117 if random(30000000)<time>>3 then last:=now else 118 begin 119 tmp:=a[x]; a[x]:=a[y]; a[y]:=tmp; 120 end; 121 inc(time); 122 end; 123 end; 124 end; 125 end.