初看此题时,显然大家都想到了贪心+搜索+剪枝,但是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.