蓝桥杯 剪邮票 DFS (不错的题目)

 

剪邮票

如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)
比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。

请你计算,一共有多少种不同的剪取方法。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;

const int maxn = 14;
int maze[3][4];
int num[6];
int have[13];          // 用了对从全排列中 选出来 5个数 ,进行 标志 (回溯: 标志为true后,需要换回false) 
bool used[3][4];       //这个是 从全排列中选出来5个数,进行DFS搜索是否连续用的标志数组 , 标志为true后,不需要换回false 
bool visit[13];        //进行全排列用的标志数组 
int ans;
int Count = 0;
int dir[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};  //方向 

void init();
void dfs_find(int r, int c);
bool judge(int r, int c);
void solve();

void init()
{
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            maze[i][j] = i*4 + j + 1;
        }
    } 
}

bool judge(int r, int c)
{
    return (r >= 0 && r < 3) && (c >= 0 && c < 4);
}

void dfs_find(int r, int c)
{
    for (int i = 0; i < 4; i++)
    {
        int nx = r + dir[i][0], ny = c + dir[i][1];
        //这个位置不是have标记位置(have里标记过的元素,是全排列找到的五个元素),或者已经访问过了 
        if (judge(nx, ny) && !used[nx][ny] && have[maze[nx][ny]]) {  
            used[nx][ny] = true;    //不需要换回 false, 除非出现另外一组5个数的时候 
            Count++;                //连续的数++ 
            dfs_find(nx, ny);       //对一个点进行DFS 
        }
    }
}

void solve()
{
    memset(have, 0, sizeof(have));
    memset(used, false, sizeof(used));
    for (int i = 1; i <= 5; i++) {
        have[ num[i] ] = 1;        //对5个数的位置进行标记 
    }
    
    for (int i = 0; i < 12; i++)
    {
        int r = i / 4;    //对应行
        int c = i % 4;    //对应列 
        if ( have[maze[r][c]] )     // 对找到的 5个数字(被标记1的),开始DFS搜索 
        {
            Count = 1;              //开始为1 
            used[r][c] = true;      //由标记的第一个数开始, 向其他标记的数 进行DFS, 找到则 Count++ 
            dfs_find(r, c);
            break;
        }
    }
    if (Count == 5) {               //全排列找到的5个数, 是相邻的, 则ans++ 
        ans++;
    }
}
//创建5个数的组合 
void Start_DFS(int cur)
{
    if (cur == 6)   //找到了5个数 
    {
        solve();    //对找到的5个数,进行排列组合 
        return;
    }
    
    //1到12 这 12个数 挨个遍历 -- 并进行DFS搜索 
    for (int i = num[cur - 1] + 1; i < 13; i++)    
    {
        if (!visit[i])          
        {
            visit[i] = true;
            num[cur] = i;       //存数,  对12个数进行全排列,从中选5个数,再进行排列组合 
            Start_DFS(cur + 1); //下一个数
            visit[i] = false;   
        }
    } 
}

int main()
{
    init();
    Start_DFS(1);
    printf("%d\n", ans);
}

 

算法思路:

1. 先全排列,从全排列1~12,从中选5个数,进行排列组合;

2. 对选中的5个数标志;

3. 循环找到第一个 第一个被标志的位置,并DFS递归寻找,他的dir方向的数字,是否是从全排列中选出来的其他数字.

详细看注释.

参考了这篇博客:http://blog.csdn.net/wuxiushu/article/details/51207533

posted @ 2017-04-05 21:13  douzujun  阅读(621)  评论(0编辑  收藏  举报