H 幻方變換(puzzle)(NYIST 2019年校賽)

  • H 幻方變換(puzzle)(NYIST 2019年校賽)

如果一個 3 × 3 的矩陣中,整數 1-9 中的每個都恰好出現一次,我們稱這個矩陣爲一個幻 方。

我們可以對一個幻方進行一些操作。具體來說,我們可以

• 選擇幻方的一行,整體向右移動一格,並將最右側的數字移到最左邊;或者

• 選擇幻方的一列,整體向下移動一格,並將最下側的數字移到最上面。

例如,下面兩個操作分別是一種合法的行操作和列操作:

img

顯然,一個合法的幻方經過一次操作後一定還是合法的幻方。

給定幻方的初始狀態,請問,最少要經過多少次變換,才能變成最終狀態?

輸入描述:

第一行一個整數 T (1 ≤ T ≤ 200000),表示測試用例的數量。

接下來有 T 組測試用例,每組測試用例前有一個空行。每組樣例的前 3 行爲幻方的初始狀態,後 3 行爲幻方的最終狀態。每行的數字之間沒有空格。

保證初始狀態和最終狀態都是合法的幻方。

輸出描述:

對於每組測試用例在一行內輸出一個整數,表示答案。如果不可能從起始狀態轉變爲最終狀態,輸出 impossible。

樣例輸入:

4

123
456
789
231
456
789

457
213
689
257
361
489

927
641
358
297
651
384

123
456
789
123
456
789

樣例輸出:

2
3
impossible
0

分析:

​ $廣搜即可。不過因爲是t組輸入,即每一次廣搜的最壞次數爲9!,故每一次都廣搜的話會超時 $

​ 我們可以預處理出123 456 789 到所有狀態的的最小次數,然後對於題目要求輸入的初始狀態和最終狀態,只需把初始狀態對應位置的數字都對應爲123 456 789,並且把最終狀態的按照這種規則轉化一下。那麼只需求123 456 789這種狀態到轉換後的最終狀態的最短距離即可。因爲預處理了,可以O(1)時間內求出來。

故可分爲下列步驟

  1. 預處理123 456 789到其他所有狀態的最少操作次數(廣搜即可)

    ​ 需要注意以下問題

    • 對於幻方的狀態可以用每一行拼接後的字符串表示
    • 定義幻方旋轉第ii行和旋轉第ii列的操作
  2. 將輸入的最初狀態轉換爲123 456 789轉換並記錄下規則

  3. 將輸入的最終狀態按照 第二步 的規則轉化爲可以求的最終狀態

  4. O(1)出答案即可

代碼:

#include<bits/stdc++.h>
#define mset(a,b)   memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
struct HuanF{
    string s;
    int step;
    HuanF Row_right(int k)//第k行向右旋轉
    {
        HuanF ans;
        ans.s=s;
        char c=ans.s[3*k+2];
        ans.s[3*k+2]=ans.s[3*k+1];
        ans.s[3*k+1]=ans.s[3*k];
        ans.s[3*k]=c;
        return ans;
    }
    HuanF  Colu_down(int k)
    {
        HuanF ans;
        ans.s=s;
        char c=ans.s[k+6];
        ans.s[k+6]=ans.s[k+3];
        ans.s[k+3]=ans.s[k];
        ans.s[k]=c;
        return  ans;
    }
    void Print()
    {
        for(int i=0;i<s.length();++i)
        {
            printf("%c%c",s[i],i%3==2?'\n':' ');
        }
        printf("step=%d\n",step);
    }
};
bool operator <(const HuanF &a ,const HuanF& b)
{
        return a.s<b.s;
}
map<HuanF,int> book;//記錄有沒有出現過
map<HuanF,int> dis;//記錄最小值
void init()
{
    int times=0;
    HuanF now;
    now.s="123456789";
    now.step=0;
    queue<HuanF> mmp;
    mmp.push(now);
    book[now]=1;
    dis[now]=0;
    while(!mmp.empty())
    {
        HuanF mm=mmp.front();
        mmp.pop();
        for(int i=0;i<3;++i)
        {
            HuanF Net=mm.Colu_down(i);
            if(!book[Net])
            {
                dis[Net]=mm.step+1;
                Net.step=mm.step+1;
                book[Net]=1;
                mmp.push(Net);
            }
            Net=mm.Row_right(i);
            if(!book[Net])
            {
                dis[Net]=mm.step+1;
                book[Net]=1;
                Net.step=mm.step+1;
                mmp.push(Net);
            }
        }
    }
}
int Hash[10];//將魔方中的數字 分別對應 1 2 3 4 5
int main()
{
    int t;
    init();
    scanf("%d",&t);
    char ss[10];
    char zz[10];
    while(t--)
    {
        for(int i=0;i<3;++i)
        {
            scanf("%s",ss+(i*3));
        }
        for(int i=0;i<9;++i)
        {
            Hash[ss[i]-'0']=i+1;
        }
        for(int i=0;i<3;++i)
        {
            scanf("%s",zz+(i*3));
        }
        for(int i=0;i<9;++i)
        {
            zz[i]=Hash[zz[i]-'0']+'0';
        }
        zz[9]='\0';
        HuanF ret;
        ret.s=string(zz);
        if(ret.s!="123456789"&&dis[ret]==0)
        {
            cout<<"impossible"<<endl;
        }
        else
            cout<<dis[ret]<<endl;
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章