【CQOI2013】新數獨 搜索

CQOI2013 NKOJ 2194 新數獨

題目描述

下面是一個沒有數字,只有大小關係(沒錯!那些尖角都是“大於”符號!)的數獨:

這裏寫圖片描述
除了大小關係外(注意相鄰格子不能相同),還需要滿足通常的數獨規則:
每個格子都是1~9 的數字
每行都是1~9的排列
每列都是1~9的排列
每個3*3的子矩陣(上圖中用粗線隔開,一共有3*3個這樣的子矩陣)都是1~9的排列
爲了美觀,每個3*3子矩陣的所有12對相鄰格子的大小關係都將給出。

輸入格式

輸入一共15行,包含一個新數獨的實例。第奇數行包含左右方向的符號(<和>),第偶數行包含上下方向的符號(^和v)。

輸出格式

輸出包含9行,每行9個1~9的數字,以單個空格隔開。輸入保證解惟一。

樣例輸入

> < < < > <
v ^ v v ^ v ^ ^ v
< < < > < <
v ^ v ^ v v ^ ^ v
< < < < > >
< > > > < >
v v ^ ^ v ^ ^ v v
< > > < > >
^ v v v ^ v v ^ v
> < < > > >
< > > > > <
v v v v ^ ^ ^ ^ ^
> < < < < <
^ ^ ^ ^ ^ v v v ^
> > < > < <

樣例輸出

5 3 9 4 6 8 2 1 7
2 4 8 1 9 7 3 5 6
1 6 7 2 3 5 9 8 4
6 8 1 7 4 2 5 9 3
3 7 5 9 1 6 8 4 2
9 2 4 5 8 3 7 6 1
7 9 6 8 2 1 4 3 5
4 1 2 3 5 9 6 7 8
8 5 3 6 7 4 1 2 9


這道題處理輸入其實比較噁心,由於markdown的格式問題體現不出來。

其實是一道比較水的搜索,但是由於自己太菜了還是寫一寫。

處理剪枝的問題時,不要每遇到一個狀態就檢查已經填好的所有數是否滿足所有約束條件。可以發現,只需要每填一個數就判斷一下它所在行是否滿足條件、所在列是否滿足條件、所在九宮格是否滿足條件、上邊和左邊是否滿足大小關係(從上到下,從左到右填數)即可。

此外,在當前狀態判斷出能轉移到的合法狀態,而不是轉移到了狀態再判斷。因爲大小關係實際上是較強的約束條件,先判斷合法狀態可以減掉很大部分枝,而轉移後再判斷就沒有這個優勢。


代碼:

#include<stdio.h>
#include<cstring>

int id[105][105],rel[105][105],X[105],Y[105],Map[105][105],be[15][15];
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};

bool R[15][15],C[15][15],B[15][15];

bool Find;

void DFS(int cur)
{
    if(Find)return;

    int i,j,x,y,a,b,k,tx,ty,tmp,ida,idb;
    bool mark[10];//標記哪些數字一定不能填

    if(cur==82)
    {
        Find=true;
        for(i=1;i<=9;i++,putchar('\n'))
        for(j=1;j<=9;j++)printf("%d ",Map[i][j]);
        return;
    }

    memset(mark,false,sizeof(mark));

    x=X[cur];y=Y[cur];a=be[x][y];
    for(i=1;i<=9;i++)if(R[x][i]||C[y][i]||B[a][i])mark[i]=true;//已經出現過,不能填

    ida=id[x][y];
    for(k=0;k<4;k++)
    {
        tx=x+dx[k];ty=y+dy[k];
        if(tx&&ty&&tx<=9&&ty<=9)
        {
            if(id[tx][ty]>cur)continue;
            idb=id[tx][ty];
            if(rel[ida][idb]==0)continue;
            if(rel[ida][idb]==1)for(i=1;i<=Map[tx][ty];i++)mark[i]=true;
            else for(i=Map[tx][ty];i<=9;i++)mark[i]=true;//約束條件強剪枝
        }
    }

    for(i=1;i<=9;i++)
    {
        if(mark[i])continue;
        Map[x][y]=i;R[x][i]=true;C[y][i]=true;B[a][i]=true;
        DFS(cur+1);
        R[x][i]=false;C[y][i]=false;B[a][i]=false;
    }
}

int main()
{
    int i,j,k,l,cnt;
    char c,s[6][20];

    for(cnt=0,i=1;i<=9;i++)
    for(j=1;j<=9;j++)id[i][j]=++cnt,X[cnt]=i,Y[cnt]=j;

    for(k=1;k<=3;k++)
    for(i=1;i<=5;i++)
    {
        for(j=1;j<=18;j++)s[i][j]=getchar();
        for(j=1;j<=18;j++)
        {
            int x,y,tx,ty,ida,idb;

            if(s[i][j]=='>')
            {
                x=(k-1)*3+(i+1)/2;y=(j+1)/2;
                tx=x;ty=y+1;
                ida=id[x][y];idb=id[tx][ty];
                rel[ida][idb]=1;
                rel[idb][ida]=-1;
            }
            if(s[i][j]=='<')
            {
                x=(k-1)*3+(i+1)/2;y=(j+1)/2;
                tx=x;ty=y+1;
                ida=id[x][y];idb=id[tx][ty];
                rel[ida][idb]=-1;
                rel[idb][ida]=1;
            }
            if(s[i][j]=='^')
            {
                x=(k-1)*3+(i+1)/2;y=(j+1)/2;
                tx=x+1;ty=y;
                ida=id[x][y];idb=id[tx][ty];
                rel[ida][idb]=-1;
                rel[idb][ida]=1;
            }
            if(s[i][j]=='v')
            {
                x=(k-1)*3+(i+1)/2;y=(j+1)/2;
                tx=x+1;ty=y;
                ida=id[x][y];idb=id[tx][ty];
                rel[ida][idb]=1;
                rel[idb][ida]=-1;
            }
        }
    }//處理輸入,寫得渣。事實上完全可以不開rel數組

    for(i=1;i<=3;i++)
    for(j=1;j<=3;j++)
    {
        int x,y,tmp;
        x=(i-1)*3+1;y=(j-1)*3+1;tmp=(i-1)*3+j;
        for(k=0;k<3;k++)
        for(l=0;l<3;l++)
        {
            be[x+k][y+l]=tmp;
        }
    }//be記錄處在的九宮格的編號

    DFS(1);
}
發佈了105 篇原創文章 · 獲贊 23 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章