如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下 55 张来,要求必须是连着的。
(仅仅连接一个角不算相连) 比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。
请你计算,一共有多少种不同的剪取方法。
思路:可先算出12个数中有多少个5个数的组合(全排列),再查看是否相连;
通过观察可发现,横向每个数相差1(5,6,7,8)
纵向上每个数相差4(1,5,9)(3,7,11)
代码:
(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个数的状态,判断其中某个数是否已经用队列处理过
int p=0;//进队列的个数。如果5个数都进过队列,说明这5个数连通
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;
}
(并查集做法)
#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;
}
全排列代码:
#include <iostream>
#include <vector>
using namespace std;
int num[12],m=5,add=0;
vector<int>chosen;
void calc(int x){
if(chosen.size()>m||chosen.size()+(12-x+1)<m){
return;
}
if(x==13){
add++;
return;
}
calc(x+1);
chosen.push_back(x);
calc(x+1);
chosen.pop_back();
}
int main()
{
for(int i=1;i<=12;i++){
num[i-1]=i;
}
calc(1);
cout<<add;
return 0;
}
#include <iostream>
using namespace std;
int star[]={1,2,3,4,6,7,8,9,11,12,13,14};
int num=0; //统计排列的个数
void Perm(int begin,int end){
if(begin == 5) //得到一个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); //求从第0个数到第11个数的全排列。
cout << "total arrange=" << num/120 <<"\n";
//注意5个数的排列不需要有序,所以除以120,这120个都是重复的。
return 0;
}