剪邮票
题目:


核心:
排列+判断连通性
求排列的方法:
判断连通性:
(1)假设裁剪的部分为2,6,7,11,12,
将2作为起点加入队列 .
(2)从2开始,遍历四个方向:上下左右 .
如果下一位置的数在裁剪部分当中,
则加入队列,否则不加入队列.
(3)队列为空后,如果进入队列的元素总数为5,则这5个数是连通的
注意:
由于使用的是一维数组,如果是+1,-1得到下一个数的位置的话,需要额外判断是否跨行

去重:
[1,2,3,4,5]与[2,1,3,4,5]两种排列:同一种裁剪
最后答案除以5!即可
代码:
#define M 12
int a[M]={1,2,3,4,5,6,7,8,9,10,11,12};
int cpy[M]={1,2,3,4,5,6,7,8,9,10,11,12};
int ans;
bool isIna(int idx)//判断该数在不在裁剪部分
{
for(int i=0;i<5;++i){//裁剪部分 存储在 a[0,4]
if(cpy[idx]==a[i])
return true;
}
return false;
}
int gets()//get-start 获得剪下来的邮票 起始位置
{
for(int i=0;i<12;++i){
if(a[0]==cpy[i])//a[0-4]:这次剪下来的邮票,比如[2,6,7,11,12]这五张
return i;//找到[2]这张邮票的原始下标位置(为1)
}
}
bool bfs()
{
//cnt:这次bfs扩散了几张剪下来的邮票
//tx:从head向上下左右走的下一个位置(数组下标)
int cnt=0,tx,s,book[M]={0},nxt[4]={1,4,-1,-4};
//队列存储的是 裁剪的数 在原一维数组中的位置(下标)
queue<int> q;
//(从剪下来的邮票中)获得起点
s=gets();
q.push(s);
book[s]=1;
cnt++;
while(!q.empty()){
auto head=q.front(); q.pop();
for(int i=0;i<4;++i){
tx=head+nxt[i];
if(tx<0||tx>11) continue;
//判断跨行
//注意:也可以改为if(head%4!=tx%4&&head/4!=tx/4)
//本质:只有+1,-1时跨行扩散不合法
// 在+4,-4时跨行合法
if(i==0||i==2)
if(head/4!=tx/4) continue;
if(book[tx]==0&&isIna(tx)){//如果没搜过 且 在裁剪部分
q.push(tx);//入队
book[tx]=1;
cnt++;
}
}
}
return cnt==5;
}
void getA(int s,int t)//12选5 获得排列
{
if(s==5){//获得一个排列 存储在a[0,4]
if(bfs()) ans++;//如果连通 ans++
return ;
}
for(int i=s;i<=t;++i){
swap(a[s],a[i]);
getA(s+1,t);
swap(a[s],a[i]);
}
}
int main()
{
getA(0,11);
cout<<ans/120;//去重
return 0;
}

浙公网安备 33010602011771号