蓝桥杯第八届:方格分割 排列组合 or dfs

题目

6x6的方格,沿着格子的边线剪开成两部分。
要求这两部分的形状完全相同

如图:p1.png, p2.png, p3.png 就是可行的分割法。
p1p2p3
试计算:
包括这3种分法在内,一共有多少种不同的分割方法。
注意:旋转对称的属于同一种分割法。

答案:509

方法一:dfs

首先我们将边编号为0,1,2,3,4,5,6
p4
然后从正中心开始分割
p5
每次都是分割一条边时,与其对称边也要分割,如图:
在这里插入图片描述p7
每条边有四个方向可以走,dfs中只需用一个标记数组将走过的边及其对称边标记,同时枚举四个方向,dfs一遍后要将标记的边都恢复为0,当到达边界时,ans++并且return,最后算出来后别忘了除以4避免重复

#include <iostream>

using namespace std;
typedef long long ll;
const int N = 6;
ll ans;
bool mp[N+1][N+1];
int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
void dfs(int x,int y)
{
    if( x == 0 || y == 0 || x == N || y == N)/*如果到达边界证明裁剪完成*/
    {
        ans++;
        return;
    }
    for(int i = 0; i < 4 ; i++)
    {
        int tx = x + dir[i][0];
        int ty = y + dir[i][1];
        if(mp[tx][ty]) continue;/*如果已经裁剪过*/
        mp[tx][ty] = true;/*如果还未被裁剪 则裁剪次边并标记*/
        mp[N - tx][N - ty] = true;/*同时还要裁剪起对称边*/
        dfs(tx,ty);
        mp[tx][ty] = false;
        mp[N - tx][N - ty] = false;
    }
}
int main()
{
    mp[N / 2][N / 2] = 1;
    dfs(N / 2,N / 2);
    cout << ans / 4 << endl;/*由于旋转对称都是相同的所以正确结果应该是ans除以4*/
    return 0;
}

方法二:排列组合

与dfs的思想差不多,不过这道题应用了排列组合的思想,这道题的目的是一个36个网格的图分为两个旋转对称的图,即一个图有18个网格,就是在36的网格个里面找两个平分连通图的个数,那么我们只需要找到一个裁剪为18个网格的图后判断其连通性即可,同时要注意,这道题要两个部分一起判断,比如当你剪裁了一半的时候要判断是否有两个连通的图,详见代码
ps:虽然这道题写出来比dfs要慢一些,但这种排列组合以及连通性的思想是值得学习的。

#include <iostream>
#include <cstring>

using namespace std;

const int n = 6;
int visited[n][n];/*0表示没有被剪裁,1表示裁剪 2表示裁剪的对立面*/
int ans;
void mark(int a[][n],int i,int j)/*将与a[i][j]连通的格子都标记为0*/
{
    int tag = a[i][j];
    a[i][j] = 0;
    if(i - 1 >= 0 && a[i - 1][j] == tag) mark(a,i - 1,j);/*上*/
    if(i + 1 < n && a[i + 1][j] == tag) mark(a,i + 1, j);/*下*/
    if(j - 1 >= 0 && a[i][j - 1] == tag) mark(a,i,j - 1);/*左*/
    if(j + 1 < n && a[i][j + 1] == tag) mark(a,i,j + 1);/*右*/
}
bool check(int a[][n])
{
    int link_num = 0;/*连通区域的数量*/
    for(int i = 0 ; i < n ; i++)
    {
        for(int j = 0; j < n ; j++)
        {
            if( a[i][j] > 0 )/*证明有连通区域*/
            {
                link_num++;
                mark(a,i,j);/*将此点的连通区域都标记为0*/
            }
        }
    }
    return (link_num == 2);/*如果连通区域有两个*/
}
void dfs(int s,int k)/*s表示裁剪的起点,k表示已经裁剪的个数*/
{
    if(k >= n * n / 2)/*如果剪裁了18个格子*/
    {
        int temp[n][n];
        memcpy(temp,visited,sizeof(visited));
        if(check(temp))/*如果有两个连通区域的*/
        {
            ans++;
        }
        return;
    }
    /*将二维的方格变成一维方便进行排列组合*/
    for(int p = s; p < n * n ; p++)
    {
        int i = p / n, j = p % n;
        if(!visited[i][j] && !visited[n - 1 - i][n - 1 - j])
        {/*将裁剪的格子与其对立面一起裁剪*/
            visited[i][j] = 1,visited[n - 1 - i][n - 1 - j] = 2;
            dfs(p + 1,k + 1);
            visited[i][j] = 0, visited[n - 1 - i][n - 1 - j] = 0;
        }
    }
}
int main()
{
    dfs(0,0);
    cout << ans / 4 << endl;/*不要忘了除以4*/
}

发布了18 篇原创文章 · 获赞 4 · 访问量 315

猜你喜欢

转载自blog.csdn.net/leslie___/article/details/105658420