2017.1.12【初中部 】普及組模擬賽C組 簡單遊戲 題解
原題:
題目描述:
Charles和sunny在玩一個簡單的遊戲。若給出1~n的一個排列A,則將A1、A2相加,A2、A3相加……An-1、An相加,則得到一組n-1個元素的數列B;再將B1、B2相加,B2、B3相加,Bn-2、Bn-1相加,則得到一組n-2個元素的數列……如此往復,最終會得出一個數T。而Charles和sunny玩的遊戲便是,Charles給出n和T,sunny在儘可能短的時間內,找到能通過上述操作得到T且字典序最小的1~n的排列。(sunny大聲說:“What an easy game!”,接著幾下就給出瞭解),Charles覺得沒意思,就想和你玩,當然,你可以用一種叫做“電子計算機”的東西幫你。
輸入:
本題有多組資料,對於每組資料:一行兩個整數n(0
輸出:
對於每組測試資料輸出一行n個整數,用空格分開,行尾無多餘空格,表示求出來的滿足要求的1~n的一個排列。
樣例輸入:
4 16
3 9
0 0
樣例輸出:
3 1 2 4
1 3 2
樣例解釋:
開始排列: 3 1 2 4
第一次操作:3+1=4 1+2=3 2+4=6
得到:4 3 6
第二次操作:4+3=7 3+6=9
得到:7 9
最後操作: 7+6=16
得到: 16
資料範圍限制:
資料保證有解。
對於30%的資料,保證該組裡的每個N都不超過10。
對於100%的資料,保證有每個N不超過20,且每組資料的個數不超過10。
分析:
分析:題目要求1-N的一個排列A1,A2…An使得C(N-1,0)*A1+C(N-1,1)*A2+….+C(N-1,N-1)*AN=T,
即: —–①式
方法很好確定,先把C(N-1,i)求出來,然後只要把每一個位上的數確定好就可以了,所以採用深度優先搜尋的方法。
用DFS(x,y)表示當前將要確定第x個位置上數,已經確定的和為y,把每種情況都搜出來,然後在遞迴出口的位置判斷①式是否成立,不過很可惜,這種方法得不到分;
剪枝一:加一個小小的優化,就是在確定第X個數時,保證新求出來的y不能大於T,加上這個優化後,可以得40分;
剪枝二:由於C(N-1,i)=C(N-1,N-1-i),具有對稱性,題目又要求最小字典序列,所以在列舉時當x>n div 2 時,要保證a[x]>a[N+1-x],這樣程式速度會提高,但是該題還是隻能得40分;
剪枝三:當列舉到第X個數時,剩餘的N-X個數,與剩餘的N-X個係數一一對應,讓大數和大系數相乘,小數和小系數相乘可以得到最大值,讓大數和小系數相乘,小數和大系數相乘可以得到最小值,如果剩餘的值不在這個範圍內,就不要搜下去,這樣可以大大優化;
實現:
uses math;
var
i,j,a,b,c,q1,q2:longint;
v:array[1..21]of longint;
p:array[1..21]of boolean;
p1:boolean;
f:array[1..20,1..20]of longint;
o,o1:array[0..20]of longint;
procedure q(l,r:longint);
var
i,j,mid:longint;
begin
i:=l;j:=r;mid:=o[(i+j) div 2];
while i<j do
begin
while o[i]<mid do inc(i);
while o[j]>mid do dec(j);
if i<=j then
begin
o[0]:=o[i];o[i]:=o[j];o[j]:=o[0];
inc(i);dec(j);
end;
end;
if l<j then q(l,j);
if i<r then q(i,r);
end;
function pd(u,t:longint):boolean;
var
i,j,s,ans,ans1:longint;
begin
s:=0;
for i:=1 to a do
if p[i]=true then
begin
inc(s);o[s]:=i;
end;
q(1,s);o1:=o;s:=0;
for i:=u to a do
begin
inc(s);o[s]:=f[a,i];
end;
q(1,s);ans:=0;ans1:=0;j:=s;
for i:=1 to s do
begin
ans:=ans+o[i]*o1[i];
ans1:=ans1+o[i]*o1[j];
dec(j);
end;
if ans+t<b then exit(false);
if ans1+t>b then exit(false);
exit(true);
end;
procedure dg(s,ans:longint);
var
i:longint;
begin
if p1=false then exit;
if ans>b then exit;
if pd(s,ans)=false then exit;
if s=a+1 then
begin
if ans=b then
begin
p1:=false;
for i:=1 to a do write(v[i],' ');
writeln;
end;
end
else
for i:=1 to a do
if (p[i]=true)and(i>v[a+1-s]) then
begin
p[i]:=false;v[s]:=i;dg(s+1,ans+i*f[a,s]);
v[s]:=0;p[i]:=true;
end;
end;
begin
assign(input,'easy.in');reset(input);
assign(output,'easy.out');rewrite(output);
for i:=1 to 20 do
begin
f[i,1]:=1;f[i,i]:=1;
end;
for i:=3 to 20 do
for j:=2 to i-1 do
f[i,j]:=f[i-1,j]+f[i-1,j-1];
while true do
begin
readln(a,b);
if (a=0)and(b=0) then halt;
fillchar(p,sizeof(p),true);
p1:=true;dg(1,0);
end;
close(input);close(output);
end.