【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≤5060%的数据中N≤500100%的数据中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.

 

posted @ 2012-05-15 21:21  datam  阅读(303)  评论(0编辑  收藏  举报