题目描述
如【图1.jpg】,有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。
请你计算,一共有多少种不同的剪取方法。
最终代码
1. c/c++(bfs 、队列 ,不推荐)
#include<bits/stdc++.h>
using namespace std;
int star[]={1,2,3,4,6,7,8,9,11,12,13,14};
int num=0; //统计排列的个数
int b[]={-1,1,-5,+5};//上下左右4个方向。
bool bfs(void)
{
//star[0]~star[4]这前5个数是递归出来的5个数。用BFS判断它们是否连通
bool status[5]={false};
//这5个数的状态,判断其中某个数是否已经用队列处理过
//进队列的个数。如果5个数都进过队列,说明这5个数连通
int p=0;
queue<int>q;
q.push(0); //第一个进队列的数。
status[0]=true;//表示0用队列处理过了。
while(!q.empty())
{
int i=q.front();//得到队列的第一个数。
q.pop(); //弹出队列头部。
p++;
for(int j=0;j<=4;j++)
if(status[j]==false) //j没有用队列处理过。
for(int k=0;k<=3;k++) //k是上下左右4个方向
if(star[i]+b[k]==star[j])
{ //top和j在k方向连接
q.push(j);
status[j]=true;//进队列了。
}
}
if(p==5) return true;
else return false;
}
void Perm(int begin,int end)
{
if(begin == 5) {
if(bfs()==true) //用bfs判断5个数是否连通
num++;
}
else
for(int i = begin;i <= end;i++){
swap(star[begin],star[i]);
Perm(begin+1,end);
swap(star[begin],star[i]);
}
}
int main()
{
Perm(0,11);
cout<<"total arrange="<<num/120<<"\n";
return 0;
}
2. c/c++(并,查,集)
#include<bits/stdc++.h>
using namespace std;
int star[]={1,2,3,4,6,7,8,9,11,12,13,14};
int num=0;
int b[]={-1,1,-5,+5};
bool check(void)
{
//检查star[0]~star[4]这5个数是否连通
int set[5]={0,1,2,3,4};
//用并查集判断5个数是否连通。初始值有5个集合,编号为0~4
for(int i=0;i<=4;i++) //第i个数和第j个数是否相邻
for(int j=0;j<=4;j++)
for(int k=0;k<=3;k++) //k是上下左右4个方向
{
if(star[i]+b[k]==star[j])
{
int temp = set[j];
set[j] = set[i];
//把第i和第j个数放到一个集合中,这个集合就是set[i]
for(int s=0; s<=4;s++) //归并集合
if(set[s]==temp)
set[s]=set[i];
}
}
for(int i=1;i<=4;i++) //检查5个数是否属于同一个集合,即连通。
if(set[0]!=set[i])
return false;
return true;
}
void Perm(int begin,int end)
{
if(begin == 5){
if(check()==true)
num++;
}
else
for(int i = begin;i <= end;i++)
{
swap(star[begin],star[i]);
Perm(begin+1,end);
swap(star[begin],star[i]);
}
}
int main(){
Perm(0,11);
cout<<"total arrange="<<num/120<<"\n";
return 0;
}