2015年6月10日 星期三

[CF 472E] Design Tutorial: Learn from a Game

作法:

首先當$n=1$或$m=1$時暴力枚舉即可。其他情形先確認是否所有珠子的個數都是對的,如果不是那就顯然無解。

首先可以想到策略大概就是一個一個把珠子歸位。首先拿起和目標盤面右下角同色的珠子,用他來轉,然後由上到下一排一排歸位,同排時由左到右一個一個歸位,直到剩下兩排時再從左到右兩顆兩顆歸位。當我們想要把$(x,y)$的珠子歸位時,如果當前手指的位置不在這裡,並且這格和目標格的顏色一樣,那麼什麼都不用作。反之找一個和這格的目標格顏色相同的珠子,假設位於$(x_1,y_1)$,那麼此時要想辦法把$(x_1,y_1)$轉到$(x,y)$上。首先找一條$(x_1,y_1)$走到$(x,y)$的路徑(走八方位都可以),這可以用DFS完成,並且在DFS時每次只走「離終點距離最近」的那幾個後繼節點。找到這條路徑後,假設$(x_1,y_1)$走上這條路徑的下一步是$(x_2,y_2)$,那麼此時只要想辦法讓手指的位置走到$(x_2,y_2)$,途中要避免走到$(x_1,y_1)$,然後再和$(x_1,y_1)$交換即可。因此我們需要一個「找一格到另一格的路徑,其中避免走到另外一格」的函式,不難發現一定存在這樣的路徑,實作也是DFS就可以了(和前面講到的那個DFS可以寫在一起)。

code :



#include<stdio.h>
#include<vector>
#include<stdlib.h>
#include<cassert>
#define ABS(x) ((x)>0?(x):(-(x)))
using namespace std;
const int maxn=30+10 ;
 
struct P{int x,y;};
 
int n,m,x0,y0 ; /// now position
int G[maxn][maxn],H[maxn][maxn] ;
vector<P> ans ;
inline void push(int x,int y)
{
    ans.push_back((P){x,y}) ;
    swap(G[x0][y0],G[x][y]) ;
    x0=x ; y0=y ;
}
 
bool done[maxn][maxn] ;
int dx[]={0,1,1,1,0,-1,-1,-1} , dy[]={1,1,0,-1,-1,-1,0,1} ;
int gx , gy , ax , ay ;
bool vis[maxn][maxn] ;
bool dfs(int x,int y,vector<P> &v)
{
    vis[x][y]=1 ;
    if(x==gx&&y==gy) {vis[x][y]=0 ; return 1 ;}
    int val=1e5 ;
    vector<P> son ;
    for(int i=0;i<8;i++)
    {
        int nx=x+dx[i] , ny=y+dy[i] ;
        if(nx<0||nx>=n||ny<0||ny>=m||(nx==ax&&ny==ay)) continue ;
        if(done[nx][ny]||vis[nx][ny]) continue ;
        int nval=ABS(nx-gx)+ABS(ny-gy) ;
        if(nval<val)
            val=nval , son.clear() , son.push_back((P){nx,ny}) ;
        else if(nval==val) son.push_back((P){nx,ny}) ;
    }
    for(auto i : son)
    {
        v.push_back(i) ;
        if(dfs(i.x,i.y,v)){vis[x][y]=0 ; return 1 ;}
        v.pop_back() ;
    }
    vis[x][y]=0 ;
    return 0 ;
}
 
void getpath(int x1,int y1,int x2,int y2,int avx,int avy,vector<P> &v)
{ /// (x1,y1) -> (x2,y2) , avoid (avx,avy)
    v.clear() ;
    gx=x2 ; gy=y2 ;
    ax=avx ; ay=avy ;
    dfs(x1,y1,v) ;
    return ;
}
 
vector<P> pth ;
void Goto(int x,int y,int avx,int avy)
{ /// now position go to (x,y) , avoid (avx,avy)
    getpath(x0,y0,x,y,avx,avy,pth) ;
    for(auto i : pth) push(i.x,i.y) ;
}
 
vector<P> tmp ;
void Moveto(int x1,int y1,int x2,int y2)
{ /// move the orb at (x1,y1) to (x2,y2)
    getpath(x1,y1,x2,y2,-1,-1,tmp) ;
    for(auto i : tmp)
    {
        Goto(i.x,i.y,x1,y1) ;
        push(x1,y1) ;
        x1=i.x ; y1=i.y ;
    }
}
 
int a[maxn],b[maxn],num[maxn*maxn] ;
void solve1()
{
    if(n==1&&m==1){printf("-1\n") ; return ;}
    if(n==1) for(int i=0;i<m;i++) a[i]=G[0][i] , b[i]=H[0][i] ;
    else for(int i=0;i<n;i++) a[i]=G[i][0] , b[i]=H[i][0] ;
    int w=n*m ;
    for(int i=0;i<w;i++) for(int j=0;j<w;j++) if(i!=j)
    {
        int d=(i<j ? 1 : -1) ;
        for(int k=i;k!=j;k+=d) swap(a[k],a[k+d]) ;
        bool ok=1 ;
        for(int k=0;k<w;k++) if(a[k]!=b[k]){ok=0 ; break ;}
        if(!ok)
        {
            for(int k=j-d;k!=i-d;k-=d) swap(a[k],a[k+d]) ;
            continue ;
        }
        printf("%d\n",(j-i)/d) ;
        for(int k=i;;k+=d)
        {
            printf("%d %d\n",n==1?1:k+1,m==1?1:k+1) ;
            if(k==j) return ;
        }
    }
    printf("-1\n") ; return ;
}
 
bool check()
{
    for(int i=0;i<n;i++) for(int j=0;j<m;j++)
        num[G[i][j]]++ , num[H[i][j]]-- ;
    for(int i=0;i<=900;i++) if(num[i]) return 0 ;
    return 1 ;
}
 
main()
{
    scanf("%d%d",&n,&m) ;
    for(int i=0;i<n;i++) for(int j=0;j<m;j++)
        scanf("%d",&G[i][j]) ;
    for(int i=0;i<n;i++) for(int j=0;j<m;j++)
        scanf("%d",&H[i][j]) ;
    if(n==1 || m==1){solve1() ; return 0 ;}
    if(!check()){printf("-1\n") ; return 0 ;}
 
    for(int i=0;i<n*m;i++) if(G[i/m][i%m]==H[n-1][m-1])
        {x0=i/m ; y0=i%m ; break ;}
    push(x0,y0) ;
 
    for(int i=0;i<n-2;i++) for(int j=0;j<m;j++)
    {
        if(G[i][j]==H[i][j] && (x0!=i || y0!=j))
            {done[i][j]=1 ; continue ;}
        for(int x=i,y=j;;)
        {
            y++ ;
            if(y==m) x++ , y=0 ;
            if(G[x][y]==H[i][j] && (x0!=x||y0!=y))
                {Moveto(x,y,i,j) ; break ;}
        }
        done[i][j]=1 ;
    }
    for(int j=0;j<m;j++) for(int i=n-2;i<n;i++)
    {
        if(i==n-1&&j==m-1) break ;
        if(G[i][j]==H[i][j] && (x0!=i || y0!=j))
            {done[i][j]=1 ; continue ;}
        for(int x=i,y=j;;)
        {
            x++ ;
            if(x==n) y++ , x=n-2 ;
            if(G[x][y]==H[i][j] && (x0!=x||y0!=y))
                {Moveto(x,y,i,j) ; break ;}
        }
        done[i][j]=1 ;
    }
 
    printf("%d\n",ans.size()-1) ;
    for(auto i : ans) printf("%d %d\n",i.x+1,i.y+1) ;
}
 

沒有留言:

張貼留言