soj1111 Gnome Tetravex dfs搜索

这也是黑书上的一道例题。

先给个soj的链接:题目

【题目大意】

给定n*n(N<=5)个方块,每个方块由上下左右四个面构成。

问是否能将n*n个方块拼成任意两个相邻块的相邻面值相等。

实例如下图:

这是一个初始2*2的方块:


它可以拼成如下图形,满足要求:


分析:

这是个典型的搜索问题,属于约束性搜索。

搜索顺序从中间到两边能够比较快的剪掉一些枝。这里用bfs先计算出一个序列,再按照这个序列进行搜索。

由于可能存在完全相同的块,这里存储不同的块和每一块的数量。

通过dfs即可算出。

实事证明还是从头开始搜比较快= =

附个中间搜的代码如下:

#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <algorithm>
#include <queue>

using namespace std;

const int maxn = 32 ;
int num , cnt[maxn] , nn , n , path[maxn] , order[maxn][6] , boundary[maxn][6] , sth ;
bool vis[maxn] ;

struct node {
    int top , right , bottom , left ;
}block[maxn];

inline bool get(int &t)
{
    bool flag = 0 ;
    char c;
    while(!isdigit(c = getchar())&&c!='-') if( c == -1 ) break ;
    if( c == -1 ) return 0 ;
    if(c=='-') flag = 1 , t = 0 ;
    else t = c ^ 48;
    while(isdigit(c = getchar()))    t = (t << 1) + (t << 3) + (c ^ 48) ;
    if(flag) t = -t ;
    return 1 ;
}

void dosth(int k)
{
    queue<int> q; 
    q.push(k/2*k);
    int x , y , i = 0 ;
    memset(vis,0,sizeof(vis));
    while (!q.empty())
    {
        boundary[i][k] = 0 ;
        x = order[i][k] = q.front();    q.pop();
        if(vis[x]) continue;
        vis[x] = 1 ;
        if( x >= k ) q.push(x-k),boundary[i][k]^=1;
        if( x % k != 0 ) q.push(x-1),boundary[i][k]^=2;
        if( x % k != k-1 ) q.push(x+1),boundary[i][k]^=4;
        if( x / k < k-1 ) q.push(x+k),boundary[i][k]^=8;
        i++;
    }
}

void init()
{
    for( int i = 1 ; i <= 5 ; i++) dosth(i);
}

bool dfs(int pos)
{
    if( pos == nn ) return true ;
    for ( int i = 1 ; i < num ; i++) if( cnt[i] )
    {
        if( boundary[pos][n] & 1 && path[order[pos][n]-n] && block[path[order[pos][n]-n]].bottom != block[i].top ) continue;
        if( boundary[pos][n] & 2 && path[order[pos][n]-1] && block[path[order[pos][n]-1]].right != block[i].left ) continue;
        if( boundary[pos][n] & 4 && path[order[pos][n]+1] && block[path[order[pos][n]+1]].left != block[i].right ) continue;
        if( boundary[pos][n] & 8 && path[order[pos][n]+n] && block[path[order[pos][n]+n]].top != block[i].bottom ) continue;
        path[order[pos][n]] = i ;
        cnt[i]--; 
        if(dfs(pos+1)) return 1 ;
        cnt[i]++; 
        path[order[pos][n]] = 0 ;
    }
    return false ;
}

int main()
{
    int t , k , i , j ;
    init();
    for ( k = 1 ; get(n) && n ; k++)
    {
        num = 1 ;
        nn = n * n ;
        sth = nn - n ;
        for( i = 0 ; i < nn ; i++)
        {
            get(block[num].top);    get(block[num].right); get(block[num].bottom); get(block[num].left);
            for( j = 0 ; j < num ; j++)
                if( block[num].top == block[j].top && 
                    block[num].bottom == block[j].bottom && 
                    block[num].right == block[j].right && 
                    block[num].left == block[j].left )
                    break;
            if( j == num ) cnt[num++] = 1 ;
            else cnt[j]++;
        }
        memset(path,0,sizeof(path));
        if(dfs(0))    printf("Game %d: Possible\n\n",k);
        else printf("Game %d: Impossible\n\n",k);        
    }
}

在附个zoj1008的代码

#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <algorithm>
#include <iostream>
#include <queue>

using namespace std;

const int maxn = 232 ;
int num , cnt[maxn] , nn , n , path[maxn] ;
bool vis[maxn] ;

struct node {
    int top , right , bottom , left ;
}block[maxn];

inline bool get(int &t)
{
    bool flag = 0 ;
    char c;
    while(!isdigit(c = getchar())&&c!='-') if( c == -1 ) break ;
    if( c == -1 ) return 0 ;
    if(c=='-') flag = 1 , t = 0 ;
    else t = c ^ 48;
    while(isdigit(c = getchar()))    t = (t << 1) + (t << 3) + (c ^ 48) ;
    if(flag) t = -t ;
    return 1 ;
}

const int cx[] = {1,-1,0,0};
const int cy[] = {0,0,1,-1};

bool dfs(int pos)
{
    if( pos == nn ) return true ;
    int x = pos / n ;
    int y = pos % n ;
    for ( int i = 0 ; i < num ; i++) if( cnt[i] )
    {
        if( x && block[path[pos-n]].bottom != block[i].top ) continue;
        if( y && block[path[pos-1]].right != block[i].left ) continue;
        path[pos] = i ;
        cnt[i]--;
        if(dfs(pos+1)) return 1 ;
        cnt[i]++;
    }
    return false ;
}

int main()
{
    int t , k , i , j ;
    for ( k = 1 ; get(n) && n ; k++)
    {
        num = 0 ;
        nn = n * n ;
        for( i = 0 ; i < nn ; i++)
        {
            get(block[num].top);    get(block[num].right); get(block[num].bottom); get(block[num].left);
            for( j = 0 ; j < num ; j++)
                if( block[num].top == block[j].top && 
                    block[num].bottom == block[j].bottom && 
                    block[num].right == block[j].right && 
                    block[num].left == block[j].left )
                    break;
            if( j == num ) cnt[num++] = 1 ;
            else cnt[j]++;
        }
	bool ff = dfs(0);
	if(k>1)
		puts("");
	cout<<"Game "<<cases++<<": ";
	if(ff)
		cout<<"Possible"<<endl;
	else cout<<"Impossible"<<endl;
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值