蓝桥杯 2016省赛 C/C++ A T7


蓝桥杯 2016省赛 C/C++ A T7

知识点

全排列+dfs连通块检测

题目

剪邮票

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

image-20210412203825071

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

请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

思路

本题考虑全排列问题,可以通过递归+回溯或者next_permutation()生成全排列,然后通过检测连通块个数(dfs)判断是否合格。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

int ans = 0;

void dfs(int g[3][4],int i,int j)
{
    g[i][j]=0;
    if (i+1<=2 && g[i+1][j]==1) dfs(g,i+1,j);
    if (i-1>=0 && g[i-1][j]==1) dfs(g,i-1,j);
    if (j+1<=3 && g[i][j+1]==1) dfs(g,i,j+1);
    if (j-1>=0 && g[i][j-1]==1) dfs(g,i,j-1);
}

bool check(int arr[12])
{
    int g[3][4];
    memset(g,0,sizeof(g));
    for (int i=0;i<3;i++)
        for (int j=0;j<4;j++)
            if (arr[i*4+j]==1)
                g[i][j]=1;
    int cnt=0;
    for (int i=0;i<3;i++)
        for (int j=0;j<4;j++)
            if (g[i][j]==1)
            {
                dfs(g,i,j);
                cnt++;
            }
    return cnt==1;
}

int main()
{
    int per[]={0,0,0,0,0,0,0,1,1,1,1,1};
    do {
        if (check(per))
            ans++;
    } while(next_permutation(per,per+12));
    cout << ans << endl;
    return 0;
}
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

int a[] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};//它的每个排列代表着12选5的一个方案
int ans = 0;

void dfs(int g[3][4],int i,int j)
{
    g[i][j]=0;
    if (i+1<=2 && g[i+1][j]==1) dfs(g,i+1,j);
    if (i-1>=0 && g[i-1][j]==1) dfs(g,i-1,j);
    if (j+1<=3 && g[i][j+1]==1) dfs(g,i,j+1);
    if (j-1>=0 && g[i][j-1]==1) dfs(g,i,j-1);
}

bool check(int arr[12])
{
    int g[3][4];
    memset(g,0,sizeof(g));
    for (int i=0;i<3;i++)
        for (int j=0;j<4;j++)
            if (arr[i*4+j]==1)
                g[i][j]=1;
    int cnt=0;
    for (int i=0;i<3;i++)
        for (int j=0;j<4;j++)
            if (g[i][j]==1)
            {
                dfs(g,i,j);
                cnt++;
            }
    return cnt==1;
}

bool vis[12];

void f(int k, int path[12])
{
    if (k==12)
    {
        if (check(path))
        {
            ans++;
            return;
        }
    }
    for (int i=0;i<12;i++)
    {
        if (i>0 && a[i]==a[i-1]&&!vis[i-1]) continue; //现在准备选取的元素和上一个元素相同,但是上一个元素还没被使用
        if (!vis[i])
        {
            vis[i]=true;
            path[k]=a[i];
            f(k+1,path);
            vis[i]=false;
        }
    }
}

int main()
{
    int path[12];
    f(0,path);
    cout << ans << endl;
    return 0;
}

文章作者: notplus
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 notplus !
 上一篇
蓝桥杯 2015省赛 C/C++ A T7 蓝桥杯 2015省赛 C/C++ A T7
蓝桥杯 2015省赛 C/C++ A T7知识点有重复元素的圆排列与环排列的计数问题 全排列+特殊去重 题目 手链样式 小明有3颗红珊瑚,4颗白珊瑚,5颗黄玛瑙。他想用它们串成一圈作为手链,送给女朋友。现在小明想知道:如果考虑手链可以随意转
2021-04-12
下一篇 
全排列问题 全排列问题
全排列问题方法全排列+检查 递归+回溯(剪枝) 模板// 转换为一维问题 #include <iostream> int a[n]; int ans=0; bool check(){} void f(int k) { if (
2021-04-12
  目录