JZOJsenior1444. 交换【推荐】

来源:互联网 发布:王者荣耀kda最新算法 编辑:程序博客网 时间:2024/06/05 08:08

Description

  给定1到N的一个排列,再给定一些允许的交换方法,要求用最少的交换次数把该排列变为1,2,3,,,N。

Input

  第一行包含两个整数N(1<=N<=12)和M(1<=M<=N*(N-1)/2),表示序列的长度以及允许的交换方案。
  第二行输入1到N个初始排列情况。
  接下来M行,每行两个整数A和B描述一个允许的交换方案,表示允许把当前排列中的第A个数和第B个数进行交换,保证A和B不相同。
  输入保证一定存在解。

Output

  输出一个整数表示最少需要的交换次数。

Sample Input

输入1:
2 1
2 1
1 2

输入2:
3 2
2 1 3
1 3
2 3

输入3:
5 5
1 2 3 4 5
1 5
2 5
1 4
1 1
3 5

Sample Output

输出1:
1

输出2:
3

输出3:
0

Data Constraint

说在前面:
最近时间紧张,没时间打博客了……咳咳……

思路:
n比较小(其实再大一点这题就做不出来了),考虑搜索
dfs不可能选择,而bfs可以试试

好,那么现在我们就考虑如何bfs切掉这道题
注意一点,起始状态和终止状态都已经给出了,为了提高效率,我们用双向bfs
(由于搜索树从一棵变成两棵,双向复杂度是单向的开方级别,有兴趣看证明的可以百度)

还有一个问题,除非像最基础的倒水问题,否则bfs会出现大量没有用的状态
但是我们不可能用12维布尔数组存下出现过的状态,有一个简单的解决办法:散列表(hash)
把当前状态压成一个十进制下的十三进制数,不超过long long,所以我们把哈希也开long long即可

比赛的时候想到了双向bfs,但没想到压位+hash,只用了字符串+hash存状态,才TLE50

接下来就很easy了
从起始状态和终止状态开始扩展,只要有一个状态出现两次,直接输出退出

(由于和谐的空间限制,我的哈希开的不大,虽说是正解但T了一个点,看情况适当打表)

代码(常数大,又臭又长):

const        maxn=1000007;type        arr=array[0..12]of longint;var        que1,que2,hash1,hash2:array[0..maxn]of        record                s:int64;                sum:longint;        end;        x,y:array[0..100]of longint;        n,m,i,j,h1,t1,h2,t2:longint;        flag:boolean;        temp:int64;        a,b:arr;procedure swap(var x,y:longint);var        z:longint;begin        z:=x;        x:=y;        y:=z;end;function turn(t:arr):int64;var        tp:int64;        i:longint;begin        turn:=0;        tp:=1;        for i:=12 downto 1 do        begin                if t[i]>0 then                turn:=turn+t[i]*tp;                tp:=tp*13;        end;end;procedure return(n:int64;var t:arr);var        i:longint;begin        fillchar(t,sizeof(t),0);        for i:=12 downto 1 do        begin                t[i]:=n mod 13;                n:=n div 13;        end;end;function find1(x:int64):boolean;var        now:longint;begin        now:=x mod maxn;        if hash1[now].s=0 then exit(true);        if hash1[now].s=x then exit(false);        now:=(now+1)mod maxn;        while (hash1[now].s<>0)and(hash1[now].s<>x)do        begin                inc(now);                if now=maxn then now:=0;        end;        if hash1[now].s=0 then exit(true);        if hash1[now].s=x then exit(false);end;procedure into1(x:int64;y:longint);var        now:longint;begin        now:=x mod maxn;        if hash1[now].s=0 then        begin                hash1[now].s:=x;                hash1[now].sum:=y;                exit;        end;        now:=(now+1)mod maxn;        while (hash1[now].s<>0)and(hash1[now].s<>x) do        begin                inc(now);                if now=maxn then now:=0;        end;        hash1[now].s:=x;        hash1[now].sum:=y;end;function find2(x:int64):boolean;var        now:longint;begin        now:=x mod maxn;        if hash2[now].s=0then exit(true);        if hash2[now].s=x then exit(false);        now:=(now+1)mod maxn;        while (hash2[now].s<>0)and(hash2[now].s<>x)do        begin                inc(now);                if now=maxn then now:=0;        end;        if hash2[now].s=0 then exit(true);        if hash2[now].s=x then exit(false);end;procedure into2(x:int64;y:longint);var        now:longint;begin        now:=x mod maxn;        if hash2[now].s=0 then        begin                hash2[now].s:=x;                hash2[now].sum:=y;                exit;        end;        now:=(now+1)mod maxn;        while (hash2[now].s<>0)and(hash2[now].s<>x) do        begin                inc(now);                if now=maxn then now:=0;        end;        hash2[now].s:=x;        hash2[now].sum:=y;end;function lookfor(x:int64):longint;var        now:longint;begin        now:=x mod maxn;        if hash1[now].s=x then exit(hash1[now].sum);        while true do        begin                inc(now);                if now=maxn then now:=0;                if hash1[now].s=x then exit(hash1[now].sum);        end;end;begin                                                    readln(n,m);        flag:=true;        for i:=1 to n do        begin                read(a[i]);                if a[i]<>i then flag:=false;                b[i]:=i;        end;        if flag then        begin                writeln(0);                halt;        end;        for i:=1 to m do readln(x[i],y[i]);        h1:=0;        t1:=1;        h2:=0;        t2:=1;        que1[1].s:=turn(a);        que2[1].s:=turn(b);        repeat                inc(h1);                if h1=maxn then h1:=0;                for i:=1 to m do                begin                        return(que1[h1].s,b);                        swap(b[x[i]],b[y[i]]);                        temp:=turn(b);                        if find1(temp) then                        begin                                into1(temp,que1[h1].sum+1);                                inc(t1);                                if t1=maxn then t1:=0;                                que1[t1].s:=temp;                                que1[t1].sum:=que1[h1].sum+1;                        end;                end;                inc(h2);                if h2=maxn then h2:=0;                if not find1(que2[h2].s)then                begin                        writeln(que2[h2].sum+lookfor(que2[h2].s));                        halt;                end;                for i:=1 to m do                begin                        return(que2[h2].s,b);                        swap(b[x[i]],b[y[i]]);                        temp:=turn(b);                        if find2(temp)then                        begin                                into2(temp,que2[h2].sum+1);                                inc(t2);                                if t2=maxn then t2:=0;                                que2[t2].s:=temp;                                que2[t2].sum:=que2[h2].sum+1;                        end;                end;        until false;end.