文章目录
[蓝桥杯][2016年第七届真题]07剪邮票
题目描述:
如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)
比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。
请你计算,一共有多少种不同的剪取方法。
请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。
【图1.jpg】
【图2.jpg】
【图3.jpg】
思路:
简化条件:
先12选5 -> 12选不同的5 -> 12选不同的5而且还要连通 -> 12选不同的5而且还要连通,不能重复出现,且只有一个连通块
做法1:
五重for循环+连通判断+除以A(5,5)=120(因为五重for循环选出的5个,可以以不同排列组成120种)
1的代码:
#include<iostream>
#include<cmath>
#include<bits/stdc++.h>
using namespace std;
int ans=0;
//深搜函数(像四周扩散,没有出口,知道全部扩散完毕就效果结束)
void dfs(int g[3][4],int i,int j){
g[i][j]=0;
if((i-1)>=0&&g[i-1][j]==1) dfs(g,i-1,j);
if((i+1)<=2&&g[i+1][j]==1) dfs(g,i+1,j);
if((j-1)>=0&&g[i][j-1]==1) dfs(g,i,j-1);
if((j+1)<=3&&g[i][j+1]==1) dfs(g,i,j+1);
}
//将一维映射到二维的函数
void o2t(int g[3][4],int a){
if(a%4==0){
g[(a/4)-1][3]=1;
}
else{
g[a/4][(a%4)-1]=1;
}
}
//对每一种排可能进行判断
void check(int g[3][4],int a,int b,int c,int d,int e){
for(int i=0;i<3;i++){
for(int j=0;j<4;j++){
g[i][j]=0;
}
}
o2t(g,a);
o2t(g,b);
o2t(g,c);
o2t(g,d);
o2t(g,e);
int cut=0;//记录着一次排列的连通个数
for(int i=0;i<3;i++){
for(int j=0;j<4;j++){
if( g[i][j]==1){
dfs(g,i,j);
cut++;
}
}
}
if(cut==1) ans++;
}
int main(){
int g[3][4];//用来排序
//要的是C(12,5),给了A(12,5)
//选出5个bu重复的数
for(int a=1;a<=12;a++){
for(int b=1;b<=12;b++){
//不重复
if(b==a) continue;
for(int c=1;c<=12;c++){
if(c==a||c==b) continue;
for(int d=1;d<=12;d++){
if(d==c||d==b||d==a) continue;
for(int e=1;e<=12;e++){
if(e==a||e==b||e==c||e==d) continue;
check(g,a,b,c,d,e);
}
}
}
}
}
cout<<ans/120;//A(5,5)=120
return 0;
}
做法2:
思路:因为是填空题,所以无所谓,但是做法一明显重复了A(5,5)次的判断。所以我们可以用全排列解决这个问题。
注意点:我们用一个a[12]={0,0,0,0,0,0,0,1,1,1,1,1}。来进行全排列。所以这里不建议手写全排列,因为你按模板手写会出现很多重复,因为他把每个0看做不一样的1也是。所以我们用next_permutation()。虽然你手写然后加上一些剪枝判断也行。
2的代码:
#include<iostream>
#include<cmath>
#include<bits/stdc++.h>
using namespace std;
int a[12]={0,0,0,0,0,0,0,1,1,1,1,1};
int b[3][4];
int ans=0;
void dfs(int b[3][4],int i,int j){
b[i][j]=0;
if((i-1)>=0&&b[i-1][j]==1) dfs(b,i-1,j);
if((i+1)<=2&&b[i+1][j]==1) dfs(b,i+1,j);
if((j-1)>=0&&b[i][j-1]==1) dfs(b,i,j-1);
if((j+1)<=3&&b[i][j+1]==1) dfs(b,i,j+1);
}
void check(){
int b[3][4];
//将一维映射为二维
for(int i=0;i<3;i++){
for(int j=0;j<4;j++){
b[i][j]=a[i*4+j];
}
}
//连通判断
int cut=0;//记录这个排列下,连通块的个数
for(int i=0;i<3;i++){
for(int j=0;j<4;j++){
if(b[i][j]==1){
dfs(b,i,j);//以这个为起点的四周连通块,全部变为0(遍历)
cut++;
}
}
}
if(cut==1){
ans++;
}
}
int main(){
do{
check();
}while(next_permutation(a,a+12));
cout<<ans;
return 0;
}