2016年蓝桥杯省赛C/C++ A组-剪邮票

题目

如下图,有12张连在一起的12生肖的邮票。

现在你要从中剪下5张来,要求必须是连着的。 (仅仅连接一个角不算相连)

比如,粉红色所示部分就是合格的剪取。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

题解

全排列+递归 或 DFS+递归。


与2013年的剪格子几乎一样,但是13年的剪格子由于数据比较特殊,所以纯DFS就可以AC,但是其本质与本题是相同的,如果真正正确,那么不能纯DFS。


纯DFS只能实现依次遍历,即S型遍历,但是无法实现T型遍历,而本题很显然存在T型连通的情况,因此纯DFS遍历失效。


DFS的整体思路:
从这12个数中选取5个数(用dfs),判断选取的5个数是否满足全部连通(递归),如果满足则答案数加一,不满足就选取另外的5个数。

DFS实现从N个数中选M个数

DFS判断二维空间中是否连通


全排列的话不能对1~12进行全排列,因为全排列的本质是A(n,m),也就是说选出的m个数是有序的,这与题目的要求是不符的。因为我们需要在12个数中5个数,所以我们要对包含7个0和5个1的数组进行全排列,每个数的下标就是该数,1表示选该数,0表示不选该数。

对于每种情况,采用的递归的方法判断一下连通性。

代码

DFS
#include<bits/stdc++.h>
using namespace std;
const int N = 100;

int n = 3, m = 4, K = 5;
int vis[N], used[N];
int ans;

int getsum (int x, int y) { // 这个函数必须要传入行号和列号,不能传入降维至一维的编号 
	if (x < 0 || x >= n || y < 0 || y >= m) return 0; // 如果用一维坐标算出x和y,那么一定不会存在id%m>=m的情况,这是因为一维编号对应的可能是个非法位置的二维坐标 
	if (used[x*m+y]) return 0;
	if (!vis[x*m+y]) return 0;
	
	used[x*m+y] = 1;
	
	return 1 + getsum (x-1, y) + getsum (x+1, y) + getsum (x, y-1) + getsum (x, y+1);
}

bool check () {
	memset (used, 0, sizeof used);
	for (int i = 0;i < n*m;i ++) {
		if (vis[i]) {
			if (getsum (i/m, i%m) == K) return true;
			else return false;
		}
	}
}

void dfs (int x, int sum) {
	if (x > n*m) return ;
	if (sum == K) {
		if (check ()) ans ++;
		return ;
	}
	
	for (int i = x;i < n*m;i ++) {
		vis[i] = 1;
		dfs (i+1, sum+1);		
		vis[i] = 0;
	}
	
	/*
	vis[x] = 1;
	dfs (x+1, sum+1);
	vis[x] = 0;
	dfs (x+1, sum);
	*/
}

int main()
{
	dfs (0, 0);
	cout << ans << endl;		

	return 0;
}
全排列
#include<bits/stdc++.h>
using namespace std;

const int N = 3, M = 4, K = 5;

int a[N*M] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};
int vis[N*M];
int ans;

int getsum (int x, int y) {
	if (x < 0 || x >= N || y < 0 || y >= M) return 0;
	if (vis[x*M+y]) return 0;
	if (!a[x*M+y]) return 0;
	
	vis[x*M+y] = 1;
	
	return 1 + getsum (x+1, y) + getsum (x-1, y) + getsum (x, y-1) + getsum (x, y+1);
}

bool check () {
	memset (vis, 0, sizeof vis);
	for (int i = 0;i < N*M;i ++) 
	if (a[i]) {
		if (getsum (i/M, i%M) == K) return true;
		else return false;
	}
}

int main()
{
	do {
		if (check ()) ans ++;		
	} while (next_permutation (a, a + 12));
	cout << ans << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不牌不改

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值