1. 程式人生 > >hnust 1606 手勢密碼(DFS)

hnust 1606 手勢密碼(DFS)

題目描述

很多手機或平板電腦軟體都可以設定手勢密碼,在設定了手勢密碼後,進入程式時,首先要輸入手勢密碼。

手勢密碼最少選擇4個點,最多選擇9個點,理論上的密碼組合總共有985824種,扣除掉其中不可能完成的組合(如一些點不允許繞過),最終的可能性是389112種。可見,手勢密碼加強了軟體訪問的安全性。

下面介紹一下手勢密碼的規則(如果你熟悉手勢密碼的規則,可略過):

1)  從某一個點出發,不間斷地畫線連線4-9個點,則從起點至終點構成的有序軌跡便構成一個有效的手勢密碼。

2)  在水平、垂直及對角方向上的3個點(假設依次為A點、B點、C點),在B點是未選點的情況下,A點是繞不過B點直接與C點相連的,如點1和點3直線連線繞不過點2。同樣道理,點1和點9直線連線繞不過點5,等等。如圖3所示,如果起點是1點,則手勢密碼必為1->2->3->6->5->4->7->8->9,而不可能是1->3->6->4->7->9。

3)  在連線不間斷延展的過程中,只要還有未選的新點,就可以畫線連線到該新點,而不管是否有重疊或是交叉,即經過已選點或已連線均可。例如,2->3->1->5或1->5->2->4都是允許的。

4)  在連線不間斷延展的過程中,中間點是不允許經過2次的(除非是規則3說明的情況),終點雖然可以再連線到已選點,但卻是無效的。例如,以1點為起點,則圖4所示的手勢密碼跟圖3所示的手勢密碼是一樣的。

   

對於沒研究過手勢密碼的同學,雖然我上面囉嗦了這麼多,估計還是有疑惑的地方,乾脆簡單點說吧!

實際上,n個點能夠構成的手勢密碼種數=n個點的排列總數-不可能完成的哪些排列數。

輸入

有多行測試資料

每行包括2個數據:min和max,表示最少選擇min個點,最多選擇max個點。取值範圍為4<=min<=max<=9。輸入時min和max用空格隔開。

輸出

與每行輸入相對應,輸出可以構成的手勢密碼總數。

樣例輸入

4 4
4 9

樣例輸出

1624
389112

題意:就是我們手機的手勢密碼,比較簡單理解

解題思路:我把這個題看作是走迷宮一類的問題,每一個點可以向不同的方向走去,走完迷宮截至,有多少種走法

那就按照走迷宮的模板來,

第一 :方向dir,每一個點有上下左右4種,斜著走4中,以及”日“字走8種,總共就是16種走法

我用dir儲存

第二:對於走到邊界怎麼處理,比如  4->2->3->1是可行的,如何處理,方法是繼續向“牆壁”走一步,看是否可行

第三:優化,也就是剪枝,很明顯,此題有很大的對稱性,4個角(也就是1,3,7,9)從這幾個起點出發的步數是一樣的

2,4,6,8這幾個點出發的步數也是一樣的,5這個點就單獨處理即可

answer=\sum(step)(  dfs(stpe,1,1)*4+dfs(step,1,2)*4+dfs(step,2,2)  )         MAX>=step>=MIN 

具體看程式碼

網上還有一種解法,我就不多說了(我還沒在網上發現我這個解法的...=v=...)

#include <bits/stdc++.h>

using namespace std;

int z[10][10];
int dir[16][2]= {1,0,0,1,-1,0,0,-1,//s上下左右
                 1,1,-1,-1,-1,1,1,-1,//斜著走
                 2,1,2,-1,1,2,-1,2,-2,1,-2,-1,1,-2,-1,-2//走日字
                };//方向座標
int dfs(int step,int x,int y)
{
    int ans=0;
    if(step==1)  return 1;
    for(int i=0; i<16; i++)
    {
        int xx,yy;
        xx=x+dir[i][0];
        yy=y+dir[i][1];
        if(xx>=1 && yy>=1 && xx<=3 && yy<=3)//在3*3的迷宮裡面
        {
            if(z[xx][yy]==0)//可以走
            {
                z[xx][yy]=1;
                ans+=dfs(step-1,xx,yy);
                z[xx][yy]=0;
            }
            else//撞牆了,繼續判斷
            {
                int xxx=xx+dir[i][0];
                int yyy=yy+dir[i][1];
                if(xxx>=1 && xxx<=3 && yyy>=1 && yyy<=3 && !z[xxx][yyy])
                {
                    z[xxx][yyy]=1;
                    ans+=dfs(step-1,xxx,yyy);
                    z[xxx][yyy]=0;
                }
            }
        }
    }
    return ans;
}
int solve(int step)
{

    int ans=0;
    z[1][1]=1;
    ans+=4*dfs(step,1,1);
    z[1][1]=0;

    z[1][2]=1;
    ans+=4*dfs(step,1,2);
    z[1][2]=0;

    z[2][2]=1;
    ans+=dfs(step,2,2);
    z[2][2]=0;
    return ans;
}
int main()
{
    int MIN,MAX;
    while(cin>>MIN>>MAX)
    {
        int ans=0;
        memset(z,0,sizeof z);
        for(int i=MIN; i<=MAX; i++)//每一次的步數
            ans+=solve(i);
        cout<<ans<<endl;
    }
    return 0;
}