【NOI2009】 变换序列
题目描述
变换序列 【问题描述】 对于N个整数0, 1, ……, N-1,一个变换序列T可以将i变成Ti,其中且。,定义x和y之间的距离。给定每个i和Ti之间的距离D(i,Ti),你需要求出一个满足要求的变换序列T。如果有多个满足条件的序列,输出其中字典序最小的一个。 说明:对于两个变换序列S和T,如果存在p<N,满足对于i=0,1,……p-1,Si=Ti且Sp<Tp,我们称S比T字典序小。 【输入文件】 输入文件transform.in的第一行包含一个整数N,表示序列的长度。接下来的一行包含N个整数Di,其中Di表示i和Ti之间的距离。 【输出文件】 输出文件为transform.out。 如果至少存在一个满足要求的变换序列T,则输出文件中包含一行N个整数,表示你计算得到的字典序最小的T;否则输出”No Answer”(不含引号)。注意:输出文件中相邻两个数之间用一个空格分开,行末不包含多余空格。 【输入样例】 5 1 1 2 2 1 【输出样例】 1 2 4 0 3 【数据规模和约定】 20%的数据中N≤50; 60%的数据中N≤500; 100%的数据中N≤10000。
解法2:正序修正
program transform; const maxn=10100; var n,i,now:longint; link,d,mark:array[0..maxn] of longint; v:array[0..maxn] of boolean; edge:array[0..maxn,0..2] of longint; //================== function find(k:longint):boolean; var i,x:longint; begin if k<=now then exit(false); for i:=1 to 2 do begin x:=edge[k,i]; if not v[x] then begin v[x]:=true; if (link[x]=-1)or find(link[x]) then begin link[x]:=k; mark[k]:=x; exit(true); end; end; end; exit(false); end; //================== procedure built; var i,t1,t2:longint; begin for i:=0 to n-1 do begin t1:=i+d[i]; t2:=i-d[i]; if t1>n-1 then dec(t1,n); if t2<0 then inc(t2,n); if t1<t2 then begin edge[i,1]:=t1; edge[i,2]:=t2; end else begin edge[i,1]:=t2; edge[i,2]:=t1; end; end; end; //================== procedure fix; var i,t,j:longint; begin for i:=0 to n-1 do if mark[i]=edge[i,2] then begin now:=i; t:=link[edge[i,1]]; link[edge[i,1]]:=i; link[edge[i,2]]:=-1; mark[t]:=0; mark[i]:=edge[i,1]; fillchar(v,sizeof(v),0); if not find(t) then begin mark[t]:=edge[i,1]; link[edge[i,1]]:=t; mark[i]:=edge[i,2]; link[edge[i,2]]:=i; end; end; end; //================== procedure main; var i,t:longint; begin now:=-1; t:=0; fillchar(link,sizeof(link),255); for i:=0 to n-1 do begin fillchar(v,sizeof(v),0); if find(i) then inc(t); end; if t<n then writeln('No Answer') else begin fix; for i:=0 to n-2 do write(mark[i],' '); writeln(mark[n-1]); end; end; //================== begin assign(input,'transform.in'); reset(input); assign(output,'transform.out'); rewrite(output); read(n); for i:=0 to n-1 do read(d[i]); built; main; close(input); close(output); end.
解法3:倒序
program transform; const maxn=10100; var n,i:longint; link,d,mark:array[0..maxn] of longint; v:array[0..maxn] of boolean; edge:array[0..maxn,0..2] of longint; //================== function find(k:longint):boolean; var i,x:longint; begin for i:=1 to 2 do begin x:=edge[k,i]; if not v[x] then begin v[x]:=true; if (link[x]=-1)or find(link[x]) then begin link[x]:=k; mark[k]:=x; exit(true); end; end; end; exit(false); end; //================== procedure built; var i,t1,t2:longint; begin for i:=0 to n-1 do begin t1:=i+d[i]; t2:=i-d[i]; if t1>n-1 then dec(t1,n); if t2<0 then inc(t2,n); if t1<t2 then begin edge[i,1]:=t1; edge[i,2]:=t2; end else begin edge[i,1]:=t2; edge[i,2]:=t1; end; end; end; //================== procedure main; var i,t:longint; begin fillchar(link,sizeof(link),255); t:=0; for i:=n-1 downto 0 do begin fillchar(v,sizeof(v),0); if find(i) then inc(t); end; if t<n then writeln('No Answer') else begin for i:=0 to n-2 do write(mark[i],' '); writeln(mark[n-1]); end; end; //================== begin assign(input,'transform.in'); reset(input); assign(output,'transform.out'); rewrite(output); read(n); for i:=0 to n-1 do read(d[i]); built; main; close(input); close(output); end.