一、问题描述
如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)
比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。
请你计算,一共有多少种不同的剪取方法。
二、解题思路
解向量是5个变量,首选需要从12个数中获得5个数,可以采用组合算法获得5个数作为候选解,c(12,5)全部组合结果就是全部的解空间(没有遗漏)。然后在对候选解做连通性检测。
三、相关算法
1.组合枚举+条件测试
代码如下(示例):
#include <bits/stdc++.h>
using namespace std;
const int num[4][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; //12张邮票相对位置
int b[20]={1,2,3,4,5,6,7,8,9,10,11,12};
int a[10];
int number=0; //记录含有多少种剪邮票的方式
bool check(int a[]){
set<int> c(a,a+5);
int s=0; //记录其相关的度数
for(int i=0;i<5;i++){
int d=0; //记录其是否在某一方向上相邻
if(a[i]-4>=1&&c.count(a[i]-4)){
d++; //该邮票与上面的邮票相邻
}
if(a[i]<=12&&c.count(a[i]+4)){
d++; //该邮票与下面的邮票相邻
}
if(a[i]!=1&&a[i]!=5&&a[i]!=9&&c.count(a[i]-1)){
d++; //该邮票与左边的邮票相邻
}
if(a[i]!=4&&a[i]!=8&&a[i]!=12&&c.count(a[i]+1)){
d++; //该邮票与右边的邮票相邻
}
if(d==0){ //该邮票不与任何一张邮票相邻
return false;
}
s+=d;
}
if(s>=8){
return true;
} else{
return false;
}
}
void print(int a[]){ //输出最后的选择的5张邮票的结果
set<int> c(a,a+5);
for(int i=0;i<3;i++){
for(int j=0;j<4;j++){
if(c.count(num[i][j])){
printf("%4d",num[i][j]);
}else{
printf("%4c"," ");
}
}
cout<<endl;
}
cout<<endl;
}
void f(int q,int p){
if(p>=5){
if(check(a)){
number++;
// print(a);
}
return;
}
for(int i=q;i<12;i++){
a[p]=b[i];
f(i+1,p+1);
}
}
int main(){
f(0,0);
cout<<number<<endl;
return 0;
}
2.使用next_permutation进行求解
代码如下(示例):
#include <bits/stdc++.h>
using namespace std;
const int num[4][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
bool check(int a[]){
set<int> c(a,a+5);
int s=0; //记录其相关的度数
for(int i=0;i<5;i++){
int d=0; //记录其是否在某一方向上相邻
if(a[i]-4>=1&&c.count(a[i]-4)){
d++; //该邮票与上面的邮票相邻
}
if(a[i]<=12&&c.count(a[i]+4)){
d++; //该邮票与下面的邮票相邻
}
if(a[i]!=1&&a[i]!=5&&a[i]!=9&&c.count(a[i]-1)){
d++; //该邮票与左边的邮票相邻
}
if(a[i]!=4&&a[i]!=8&&a[i]!=12&&c.count(a[i]+1)){
d++; //该邮票与右边的邮票相邻
}
if(d==0){ //该邮票不与任何一张邮票相邻
return false;
}
s+=d;
}
if(s>=8){
return true;
} else{
return false;
}
}
void print(int a[]){ //输出最后的选择的5张邮票的结果
set<int> c(a,a+5);
for(int i=0;i<3;i++){
for(int j=0;j<4;j++){
if(c.count(num[i][j])){
printf("%4d",num[i][j]);
}else{
printf("%4c"," ");
}
}
cout<<endl;
}
cout<<endl;
}
int main(){
int vis[20]={0,0,0,0,0,0,0,1,1,1,1,1};
int a[10];
int number;
do{
int j=0;
for(int i=0;i<12;i++){
if(vis[i]==1){
a[j]=i+1;
j++;
}
}
if(check(a)){
number++;
// print(a); //输出最后选择的5张邮票
}
}while(next_permutation(vis,vis+12));
cout<<number<<endl;
return 0;
}
四、运算结果
答案:116