[蓝桥杯][2016年第七届真题]07剪邮票

[蓝桥杯][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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值