七數碼&平分石子

七數碼

有一個4*2的方框,裏面隨機填充着0-7這8個數。每次可以交換0和它相鄰的格子裏的數。比如:
這裏寫圖片描述
可以變換爲:
這裏寫圖片描述
也可以變換爲:
這裏寫圖片描述
最終希望8個數整齊地排列爲:
這裏寫圖片描述
給出T組初始狀態,求出每種狀態移動到目標狀態的最少步數。

輸入

第1行:1個整數T(1<=T<=10000)
接下來T行,每行一個數字串,表示初始狀態

輸出

輸出T行,每行1個整數,表示每個初始狀態對應的最少移動步數。

樣例輸入

3
0 1 2 3 4 5 6 7
1 0 2 3 4 5 6 7
7 6 5 4 3 2 1 0

樣例輸出

0
1
28

分析

由於多組數據,而每組數據都出現在變換之中,所以考慮在輸入前將所有狀態存起來;
所以設置初始狀態爲0,1,2,3,4,5,6,7
再依次拓展開,存每個狀態;
這裏涉及到一個知識(康託展開),計算每種狀態的康託展開值,存每種狀態;

代碼

我的做法是用數組模擬隊列,所以代碼量會比較大

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node{
    int a[8],b;
}que[300005];//隊列,用於存步數與每種狀態的數列
int T,dp[300005];//dp存狀態
int fac[]={1,1,2,6,24,120,720,5040,40320};//階乘(用於康託展開)
int head,tail;//數組模擬隊列用來刪除與插入
/*
康託展開值的計算:
從當前數開始比它小的數有n個,就讓n乘上當前(從右往左)所處位置的階乘
計算每個數,再求和

兩層循環。
第一層枚舉數列
第二層從第一層選定值開始枚舉到最後
*/
int cont(int x)//計算康託展開值
{
    int r=0;//存展開值
    for (int i=0;i<=7;i++)//選定值i
        for(int j=i+1;j<=7;j++)//枚舉後面的數
            if(que[x].a[i]>que[x].a[j])r+=fac[7-i];//比它小,就加一個階乘
    return r;
}
void swa(int &x,int &y)//最好自己寫交換函數
{
    int t=x;x=y;y=t;
}
void gai(int x,int y)//轉移狀態
{
    for(int i=0;i<=7; i++)
        que[y].a[i]=que[x].a[i];//將上一狀態賦值給計算狀態
}
int main()
{
    memset(dp,-1,sizeof(dp));//初始值
    head=tail=1;//設置隊頭隊尾
    for(int i=0;i<=7;i++)
        que[tail].a[i]=i;//賦初始狀態
    dp[0]=0;//初始狀態也算一種
    que[tail].b=0;
    tail++;
    while(head<tail)//隊列不爲空
    {
        int x;//存0的位置
        for(int i=0;i<=7;i++)//找0
            if(que[head].a[i]==0){x=i;break;}
        if(x!=0&&x!=4)//向左
        {
            gai(head,tail);//轉移狀態
            swa(que[tail].a[x],que[tail].a[x-1]);//交換
            int q=cont(tail);//計算康託展開值
            if(dp[q]==-1)//沒被計算過
            {
                dp[q]=que[head].b+1;
                que[tail++].b=que[head].b+1;
                //轉移狀態
            }
        }
        if(x!=3&&x!=7)//向右
        {
            gai(head,tail);
            swa(que[tail].a[x],que[tail].a[x+1]);
            int q=cont(tail);
            if(dp[q]==-1)
            {
                dp[q]=que[head].b+1;
                que[tail++].b=que[head].b+1;
            }
        }
        if(x<4)//向下
        {
            gai(head,tail);
            swa(que[tail].a[x],que[tail].a[x+4]);
            int q=cont(tail);
            if(dp[q]==-1)
            {
                dp[q]=que[head].b+1;
                que[tail++].b=que[head].b+1;
            }
        }
        else if(x>3)//向上
        {
            gai(head,tail);
            swa(que[tail].a[x],que[tail].a[x-4]);
            int q=cont(tail);
            if(dp[q]==-1)
            {
                dp[q]=que[head].b+1;
                que[tail++].b=que[head].b+1;
            }
        }
        head++;//拓展下一個狀態
    }
    scanf("%d",&T);//輸入數據組數
    for(int i=1;i<=T;i++)
    {
        for(int j=0;j<=7;j++)
            scanf("%d",&que[0].a[j]);//輸入數據
        printf("%d\n",dp[cont(0)]);//輸出
    }
}

平分石子

有3堆石子,初始時數量分別有A,B,C顆。每次操作是針對某兩堆,設這兩堆的當前石子數量爲X,Y且X < Y. 然後從數量多的一堆中取出X顆石子放入數量少的一堆,使數量少的一堆加倍。操作後兩堆石子的數量爲:X+X, Y-X
問:經過任意多次的操作,能否使得3堆石子的數量相等?

輸入

第1行:3個整數A,B,C (1<=A,B,C<=1000)

輸出

第1行:如果可以相等,輸出”possible”,否則輸出”impossible”

樣例輸入

10 15 35

樣例輸出

possible

提示

第1次操作:10, (15,35) => 10, 20, 30
第2次操作:20, (10,30) => 20, 20, 20

分析

這一題跟上面那一題比起來簡直水爆。
先不說這些。
這裏提供一個考試技巧,
當實在對一道題沒思路時,就用爆搜
這一題比之前那道簡單是因爲,這裏不用計算康託展開。

代碼

#include<cstdio>
#include<algorithm>
using namespace std;
int a[4];//存這三個數
bool f,q[3005][3005];//存狀態(f爲是否找到,q爲是否計算)
void dfs(int x,int y,int z)
{
    if(x==y&&y==z){f=1;return;}//滿足條件,標記找到
    if((q[x][y]||q[x][z]||q[y][z])||f)return;//計算過
    q[x][y]=q[x][z]=q[y][z]=1;//標記計算
    a[1]=x+x;a[2]=y-x;a[3]=z;//計算前兩個
    sort(a+1,a+1+3);//排序
    dfs(a[1],a[2],a[3]);
    a[1]=x;a[2]=y+y;a[3]=z-y;//計算後兩個
    sort(a+1,a+1+3);//排序
    dfs(a[1],a[2],a[3]);
    a[1]=x+x;a[2]=y;a[3]=z-x;//計算一三個
    sort(a+1,a+1+3);//排序
    dfs(a[1],a[2],a[3]);
}
int main()
{
    scanf("%d%d%d",&a[1],&a[2],&a[3]);
    if((a[1]+a[2]+a[3])%3!=0){printf("impossible\n");return 0;}//當加起來不能被3整除,就不可能構成
    sort(a+1,a+1+3);//從小到大排序,避免分類
    dfs(a[1],a[2],a[3]);
    if(f){printf("possible\n");return 0;}
    printf("impossible\n");
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章