初看此题时,显然大家都想到了贪心+搜索+剪枝,但是POJ的评测机是很不给力的,一定是TLE。所以,理所当然的,我们知道了random的威力。
先证明一下贪心算法的正确性:
策略:将这n*3个元素倒序快排一下,取前n*2个元素分为两组,最后n个元素分为一组。
证明:如果不这样选的话,那么前两组的总和必会比这样选的情况小,那么更有可能失败。证毕。
随机化算法:
每次从前两个集合中各选一个元素进行交换,如果满足条件直接输出并halt.
这个随机化的证明嘛,我也不会。
CODE
Program POJ2454;//By_Poetshy
Const
maxn=200;
Var
i,j,k,m,n :Longint;
a,rank :Array[1..maxn]of Longint;
v :Array[1..maxn]of Boolean;
all,sum,temp :Longint;
Procedure Qsort(l,r:Longint);
var i,j,k,temp:Longint;
begin
i:=l;j:=r;k:=a[(i+j)>>1];
repeat
while a[i]>k do inc(i);
while a[j]<k do dec(j);
if i<=j then
begin
temp:=a[i];a[i]:=a[j];a[j]:=temp;
temp:=rank[i];rank[i]:=rank[j];rank[j]:=temp;
inc(i);dec(j);
end;
until i>j;
if i<r then Qsort(i,r);
if l<j then Qsort(l,j);
end;
Procedure Print;
var j:Longint;
begin
for j:=1 to n do writeln(rank[j]);
for j:=n+1 to n*2 do writeln(rank[j]);
for j:=n*2+1 to n*3 do writeln(rank[j]);
end;
Function check:Boolean;
begin
if sum<=500*n then exit(false);
if all-sum<=500*n then exit(false);
exit(true);
end;
BEGIN
readln(n);
for i:=1 to n*3 do
begin
readln(a[i]);rank[i]:=i;
end;
Qsort(1,n*3);
sum:=0;m:=1;
fillchar(v,sizeof(v),0);
for i:=1 to n<<1 do inc(all,a[i]);
for i:=1 to n do
begin
inc(sum,a[i]);
v[i]:=true;
end;
randomize;
while true do
begin
if Check then
begin
Print;
halt;
end;
i:=random(n)+1;
j:=random(n)+n+1;
inc(sum,a[j]);dec(sum,a[i]);
temp:=a[i];a[i]:=a[j];a[j]:=temp;
temp:=rank[i];rank[i]:=rank[j];rank[j]:=temp;
end;
END.