HEOI2012 朋友圈
这是做的最得意的一道题,当时在考场上得了90,是因为数据中A有100++的情况,Runtime_error了。。
首先强调,本人不是用二分图,或者说不用图论算法,只涉及简单的枚举和记忆化思想。
A国的人最多只能取两个,这个大家都能看出来,当然还可能不取。
B国的人不去向常规考虑奇偶分组,这个会在后面提到。
题意抽象为求一个最大完全图,那么当我们确定一个极大完全图后,任意一个元素都可以作为代表元,给定代表元之后,通过一边O(n)的扫描就能得到这个极大完全图。
所以对于不含A国人的情况,我们只需要枚举一个B国人,把其他的B国人尽量多的加进去,这个加人的顺序与结果无关,而代表元可以使任意一个,因此可以用一个数组记录v[x]表示是否求过以x为代表元的集合。
对于有A国人的情况,枚举一个A国人,再枚举一个B国人,同理他们构成的最大完全图也就确定了,同样可以使用记忆化来降低枚举时间。
对于两个A国人的情况,一样处理即可。
程序有两个记录数组v1,v2,由于A,B的人数差距极大,用一个数组记录就需要开10*10*1500,但是事实上的数据会达到200*200*3000,极大的超内存,所以采用分开处理的方式,A较小B较大时用200*200*200的数组,A极小B很大时用50*50*3000的,内存就很充裕了。
而对于时间复杂度,为最大枚举数O(sa*sa*sb*sb),加入的v数组会让它达不到这个极限。
事实证明,时限为20S的题目,我的程序通过最大点的时间仅为2s++
如有反例,欢迎提出。
View Code
1 program friends(input,output); 2 var 3 can:array[0..4050,0..4050] of boolean; 4 a:array[0..1005] of longint; 5 b:array[0..4010] of longint; 6 stack:array[0..600] of longint; 7 v1:array[0..310,0..310,0..400] of boolean; 8 v2:array[0..50,0..50,0..3600] of boolean; 9 flag:boolean; 10 top:longint; 11 answer:longint; 12 sa,sb,m:longint; 13 cases:longint; 14 function check(x:longint):boolean; 15 var 16 i,sum:longint; 17 begin 18 sum:=0; 19 for i:=0 to 30 do 20 if (x and (1 shl i))>0 then 21 inc(sum); 22 if odd(sum) then 23 exit(true); 24 exit(false); 25 end;{ check } 26 procedure init; 27 var 28 i,j,xx,yy:longint; 29 begin 30 readln(sa,sb,m); 31 if (sa<=300)and(sb<=350) then 32 flag:=true; 33 if (sa<=20)and(sb<=3000) then 34 flag:=false; 35 fillchar(can,sizeof(can),false); 36 for i:=1 to sa do 37 read(a[i]); 38 for i:=1 to sb do 39 read(b[i]); 40 for i:=1 to m do 41 begin 42 read(xx,yy); 43 can[xx,yy+sa]:=true; 44 can[yy+sa,xx]:=true; 45 end; 46 for i:=1 to sa-1 do 47 for j:=i+1 to sa do 48 if ((a[i] xor a[j]) mod 2=1) then 49 begin 50 can[i,j]:=true; 51 can[j,i]:=true; 52 end; 53 for i:=1 to sb-1 do 54 for j:=i+1 to sb do 55 if ((b[i] xor b[j]) mod 2=0)or(check(b[i] or b[j])) then 56 begin 57 can[i+sa,j+sa]:=true; 58 can[j+sa,i+sa]:=true; 59 end; 60 fillchar(v1,sizeof(v1),false); 61 fillchar(v2,sizeof(v2),false); 62 for i:=0 to sa+sb do 63 begin 64 can[0,i]:=true; 65 can[i,0]:=true; 66 end; 67 end;{ init } 68 procedure calc(xx,yy,zz:longint); 69 var 70 i,j:longint; 71 flags:boolean; 72 begin 73 top:=0; 74 if xx<>0 then 75 begin 76 inc(top); 77 stack[top]:=xx; 78 end; 79 if yy<>0 then 80 begin 81 inc(top); 82 stack[top]:=yy; 83 end; 84 if zz<>sa then 85 begin 86 inc(top); 87 stack[top]:=zz; 88 end; 89 if flag then 90 v1[xx,yy,zz-sa]:=true 91 else 92 v2[xx,yy,zz-sa]:=true; 93 for i:=1 to sb do 94 if (i+sa<>zz) then 95 begin 96 flags:=true; 97 for j:=top downto 1 do 98 if not can[i+sa,stack[j]] then 99 begin 100 flags:=false; 101 break; 102 end; 103 if not flags then 104 continue; 105 inc(top); 106 stack[top]:=sa+i; 107 if flag then 108 v1[xx,yy,i]:=true 109 else 110 v2[xx,yy,i]:=true 111 end; 112 if top>answer then 113 answer:=top; 114 end;{ calc } 115 procedure main; 116 var 117 i,j,k:longint; 118 begin 119 answer:=0; 120 if sa=0 then 121 begin 122 for i:=1 to sb do 123 calc(0,0,i); 124 exit; 125 end; 126 if sa=1 then 127 begin 128 for i:=0 to 1 do 129 for j:=sa+1 to sb+sa do 130 if (((not v1[0,i,j-sa])and(flag))or((not flag)and(not v2[0,i,j-sa])))and(can[i,j]) then 131 calc(0,i,j); 132 exit; 133 end; 134 for i:=sa+1 to sb+sa do 135 calc(0,0,i); 136 for i:=0 to sa-1 do 137 for j:=i+1 to sa do 138 if can[i,j] then 139 for k:=sa+1 to sb+sa do 140 if (((not v1[i,j,k-sa])and(flag))or((not flag)and(not v2[i,j,k-sa])))and(can[i,k])and(can[j,k]) then 141 calc(i,j,k); 142 end;{ main } 143 procedure print; 144 begin 145 writeln(answer); 146 end;{ print } 147 begin 148 assign(input,'friends.in');reset(input); 149 assign(output,'friends.out');rewrite(output); 150 readln(cases); 151 while cases>0 do 152 begin 153 dec(cases); 154 init; 155 main; 156 print; 157 end; 158 close(input); 159 close(output); 160 end.
注:BZOJ上仅有一组数据且输入没有T,直接按一组数据做即可。
本随笔为原创,如要转载,请注明出处。