剪邮票 蓝桥杯

时间:2021-11-06 05:24:52

12张连在一起的12生肖的邮票。

现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)

比如,下图中,粉红色所示部分就是合格的剪取。

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

剪邮票 蓝桥杯

题解:

开始想直接dfs,可是会忽略好多种情况,因为dfs的结果是一笔画的,如图二直接dfs就不行。

正确的应该是先dfs 5个点,然后判断这5个点是够连通, 是否仅有1个连通分支

结果:116

方一:利用bfs判断是否连通

#include<stdio.h>
#include<string.h>

typedef struct Nod{//每个方块信息
int v;//v为0没取,v为1取了
int x;
int y;
}Node;

int ans,d[4][2]={0,1,1,0,-1,0,0,-1},vis[3][4],sum;
Node n[3][4],Q[13];

int bfs(int a,int b){
int k=0,top=0,i,j,x,y,x1,y1;
Q[k++]=n[a][b];//将n[a][b]入队列
vis[a][b]=0;
while(top<k){
x=Q[top].x;
y=Q[top].y;
for(i=0;i<4;i++){//遍历与该点相连且选取了的方块
x1=x+d[i][0];
y1=y+d[i][1];
if(x1>=0&&x1<=2&&y1>=0&&y1<=3&&vis[x1][y1]==1){
Q[k++]=n[x1][y1];//入队列
vis[x1][y1]=0;
sum++;
}
}
top++;//相当于最先进来的出队列
}
return sum;

}

int dfs(int x,int y,int num){
int i,j,x1,y1;
if(num==5){
sum=1;
for(i=0;i<3;i++){
for(j=0;j<4;j++){
vis[i][j]=n[i][j].v;
}
}
if(bfs(x,y)==5){//如果5个方块连通,加1
ans++;
}
return 0;
}
x1=x;
y1=y+1;
if(y1==4){
x1=x+1;
y1=0;
}
if(x1==3) return 0;

n[x1][y1].v=1;//该点取
dfs(x1,y1,num+1);

n[x1][y1].v=0;//该点不取
dfs(x1,y1,num);
}


int main(){
int i,j;
memset(vis,0,sizeof(vis));
for(i=0;i<3;i++){
for(j=0;j<4;j++){
n[i][j].v=0;
n[i][j].x=i;
n[i][j].y=j;
}
}
dfs(0,-1,0);
printf("%d",ans);
}

方二:找一共有几个连通分支,只有一个则正确

#include<stdio.h>
#include<string.h>

int vis[3][4],b[3][4],ans=0,d[4][2]={0,1,1,0,-1,0,0,-1};

int bfs(int x,int y){//类似bfs,找与该点相连通的点
int i,j,x1,y1;
b[x][y]=0;
for(i=0;i<4;i++){
x1=x+d[i][0];
y1=y+d[i][1];
if(x1>=0&&x1<=2&&y1>=0&&y1<=3&&b[x1][y1]==1){
bfs(x1,y1);//一直找下去,直到找完
}
}
}

int dfs(int x,int y,int sum){
int i,j,x1,y1;
if(sum==5){
for(i=0;i<3;i++){
for(j=0;j<4;j++){
b[i][j]=vis[i][j];
}
}
int cnt=0;// 记录有几个连通分支
for(i=0;i<3;i++){
for(j=0;j<4;j++){
if(b[i][j]==1){
bfs(i,j);
cnt++;
}
}
}
if(cnt==1){//只有一个连通分支,5个点是连着的
ans++;
}
return 0;
}
x1=x;y1=y+1;
if(y1==4){
x1=x+1;
y1=0;
}
if(x1==3) return 0;
vis[x1][y1]=1;//该点取
dfs(x1,y1,sum+1);

vis[x1][y1]=0;//该点不取
dfs(x1,y1,sum);
}

int main(){
memset(vis,0,sizeof(vis));
dfs(0,-1,0);
printf("%d",ans);
}