noi 2009 变换序列

本文解析了一道NOI 2009竞赛题,题目要求根据给定的整数距离找出一个字典序最小的变换序列。通过分析题目特点,采用二分匹配的方法,并结合匈牙利算法进行求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【问题描述】

       对于N个整数0, 1, ……, N-1,一个变换序列T可以将i变成Ti,其中 noi <wbr>2009 <wbr>变换序列noi <wbr>2009 <wbr>变换序列noi <wbr>2009 <wbr>变换序列,定义xy之间的距离noi <wbr>2009 <wbr>变换序列。给定每个iTi之间的距离D(i,Ti),你需要求出一个满足要求的变换序列T。如果有多个满足条件的序列,输出其中字典序最小的一个。

       说明:对于两个变换序列ST,如果存在p<N,满足对于i=0,1,……p-1Si=TiSp<Tp,我们称ST字典序小。

【输入文件】

       输入文件transform.in的第一行包含一个整数N,表示序列的长度。接下来的一行包含N个整数Di,其中Di表示iTi之间的距离。

【输出文件】

       输出文件为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

 

题目的意思就是给出xy的对应关系,求是否存在一一对应的关系,有则输出字典序最小的一组。

观察可知,给定xy的距离后,对于x最多只有四个可能的y与之对应。对于匈牙利算法的时间复杂度是O(nm),这道题就是一道赤果果的二分匹配题了。

还有一点可能会头疼的是要求字典序最小。回忆:找增光路的时候都是从最小的点开始找起。一旦找到增光路,就弹出真值。显然,每次找完第i个点,当前匹配中i对应的值一定是可行匹配中最“小”的。那么我们在找增光路的时候从n退回1增广,最后求出的匹配就是字典序最小的可行解了。还有就是用邻接表存边的时候要注意顺序,确保从字典序小的边找起。

第一次出错竟然是因为求增光路的时候没有给dfs函数赋初值,然后就。。。

 

AC CODE

 

program noi_2009_day1_transform;

var g,next:array[1..40000] of longint;

    en,a,res:array[1..10000] of longint;

    sta:array[1..10000] of boolean;

    n,i,j,tot:longint;

//============================================================================

procedure ins(x,y:longint);

begin

  inc(tot); g[tot]:=y;

  next[tot]:=en[x]; en[x]:=tot;

end;

//============================================================================

procedure init;

var i,x:longint;

begin

  readln(n);

  for i:=1 to n do

  begin

    read(x);

    if i-x<1 then ins(i,i+n-x);

    if i+x<=n then ins(i,i+x);

    if i>x then ins(i,i-x);

    if i-n+x>0 then ins(i,i-n+x);

  end;

end;

//============================================================================

function dfs(u:longint):boolean;

var i,v:longint;

begin

  i:=en[u]; sta[u]:=true;

  while i<>0 do

  begin v:=g[i];

    if (res[v]=0) or (not(sta[res[v]]) and dfs(res[v])) then

    begin

      res[v]:=u;

      exit(true);

    end; i:=next[i];

  end; exit(false);     //一开始就是这句话没有加。。。

end;

//============================================================================

begin

  assign(input,'transform.in');

  assign(output,'transform.out');

  reset(input); rewrite(output);

  init;

  for i:=n downto 1 do    //从后往前增广。

  begin

    for j:=1 to n do sta[j]:=false;

    if not(dfs(i)) then

    begin

      writeln('No Answer');

      close(input); close(output);

      halt;

    end;

  end;

  for i:=1 to n do a[res[i]]:=i;

  for i:=1 to n-1 do write(a[i]-1,' '); writeln(a[n]-1);

  close(input); close(output);

end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值