【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 又为他们标出了联系的愉快程度。如 34 的关系非
常好,因此标记他们之间的联系愉快程度为 10;而 13 是一般的朋友,则他
们的愉快程度要小一些。上面的图 1 表示一个 N=5 的联系表,其中点表示名单
上的好友,边则表示两个好友可以直接联系,边上的数字即为他们联系的愉快程
度。
小 Y 和小 Z 希望大家都能喜欢这次聚会,
因此决定在尽量最大化联系网络的
愉快程度:所谓联系网络的愉快程度,即每一对直接联系人之间的愉快程度之
和。如在图 1 中,加粗的边表示了一个让愉快程度最大联系的网络,其愉快程度
为 5+6+10+5=26。
然而,如果让某个人直接和很多的人联系,这势必会给他增添很大的负担。
因此小 Y 和小 Z 还为每个人分别设定了一个最大的直接联系人数 ki,表示在联
系网络中,最多只能有 ki 个人和 i 直接联系。
还是用图 1 的例子,若我们为 15 每个点分别加上了 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.

 

posted @ 2012-06-05 20:44  datam  阅读(529)  评论(0编辑  收藏  举报