蓝桥杯往届试题练习 剪邮票问题 (组合选取,判断是否联通)(类似枚举的思路)

剪邮票


如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)
比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。


请你计算,一共有多少种不同的剪取方法。


请填写表示方案数目的整数。

注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。


注意:这道题不可以简单地用深度优先搜索来解,否则算出的结果一定偏小。这是因为:

深度搜索只能纵向往下搜索,那么如下图这种情况是无法搜索到的,因此,这道题目要求对实际问题的分析能力很高。

从3->7->6  到了6该往哪走呢?走到5的话便无法回头走到10.同理走10的话就走不到5.




因此,此题应该用枚举的思路来做。

也就是从12个格子中选取出5个。判断它是否联通即可。

关于从12个元素怎么从中选取5个,请看鄙人另一篇:

https://blog.csdn.net/linruier2017/article/details/79726231


程序源代码大致如下:



#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
int a[12];
int dx[4]={-1,1,0,0};int dy[4]={0,0,-1,1};
bool ok(int get[],int k)
{
for(int i=0;i<k;i++)if(get[i]>=get[k])return false;
return true;
}


bool together(int n1,int n2)
{
int x1=n1/4;int y1=n1%4;
int x2=n2/4;int y2=n2%4;
for(int i=0;i<4;i++)
{
if(x1+dx[i]==x2&&y1+dy[i]==y2)return true;
}
return false;
}
bool liantong(int get[],int len)
{
int pos=0;
set<int>s;s.insert(get[pos]);
set<int>::iterator it;
for(it=s.begin();it!=s.end();it++)
{
for(int i=1;i<len;i++)
{
if(together(*it,get[i])&&s.count(get[i])==0)
{
s.insert(get[i]);
it=s.begin();
}
}
}
for(int i=0;i<len;i++)if(s.count(get[i])==0)return false;
return true;
}
int main()
{
int sum=0;int total=0;
//回溯法从12个格子里面选取5个出来
int get5[5];
for(int i=0;i<5;i++)get5[i]=-1;
int k=0;int c[5];
for(int i=0;i<5;i++)c[i]=0;
while(k>=0)
{
while(c[k]<12)
{
get5[k]=c[k]++;
if(ok(get5,k)&&k==4)
{
total++;
if(liantong(get5,5))//判断选择的5张邮票是否联通 
{
sum++;
}
}
else if(ok(get5,k)&&k<4)k++;
}
get5[k]==-1;
c[k]=0;
k--;

cout<<"共有"<<total<<"种组合"<<endl;
cout<<"符合要求的有"<<sum<<"种"<<endl;
return 0;



看到的大神如看到代码的不足或失误,敬请留言指出,不胜感激!

原文地址:https://www.cnblogs.com/linruier/p/9485207.html