题目:滑动解锁 蓝桥杯

时间:2021-10-27 11:02:02
滑动解锁是智能手机一项常用的功能。你需要在3x3的点阵上,从任意一个点开始,反复移动到一个尚未经过的”相邻”的点。这些划过的点所组成的有向折线,如果与预设的折线在图案、方向上都一致,那么手机将解锁。

所谓两个点“相邻”:当且仅当以这两个点为端点的线段上不存在尚未经过的点。

此外,许多手机都约定:这条折线还需要至少经过4个点。

为了描述方便,我们给这9个点从上到下、从左到右依次编号1-9。即如下排列:

1 2 3
4 5 6
7 8 9

那么1->2->3是非法的,因为长度不足。
1->3->2->4也是非法的,因为1->3穿过了尚未经过的点2。
2->4->1->3->6是合法的,因为1->3时点2已经被划过了。

某大神已经算出:一共有389112种不同的解锁方案。没有任何线索时,要想暴力解锁确实很难。
不过小Hi很好奇,他希望知道,当已经瞥视到一部分折线的情况下,有多少种不同的方案。
遗憾的是,小Hi看到的部分折线既不一定是连续的,也不知道方向。

例如看到1-2-3和4-5-6,
那么1->2->3->4->5->6,1->2->3->6->5->4, 3->2->1->6->5->4->8->9等都是可能的方案。

你的任务是编写程序,根据已经瞥到的零碎线段,求可能解锁方案的数目。

输入:
每个测试数据第一行是一个整数N(0 <= N <= 8),代表小Hi看到的折线段数目。
以下N行每行包含两个整数 X 和 Y (1 <= X, Y <= 9),代表小Hi看到点X和点Y是直接相连的。

输出:
对于每组数据输出合法的解锁方案数目。

例如:
输入:
8
1 2 2 3
3 4 4 5
5 6 6 7
7 8 8 9

程序应该输出:
2

再例如:
输入:
4
2 4
2 5
8 5
8 6

程序应该输出:

258


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

const int maxn=10;
/*
这道题我的思路是把所有联通的线都连好,找到起点和终点,然后再来一个全排列,
每次只需要检查两个相邻的线的尾可不可以和下一条线的头部相连就好了,
但是存在一个问题,就是这条线他不一定是按照你的意愿所排列的,只能是一条单向边,
可是就会忽略反着的情况,这就很尴尬,导致我们的全排列就很难受

我们可能直接去筛选符合条件的情况然后往下走,可能有点问题,但是我们可以先
暴力出来一个答案,然后我们去检测,他是不是符合我们的条件,
不一定非要找到满足条件的,我们才往下走,我们也是可以先走一步,
然后再去检测,虽然比较浪费,但是思路还是比较好想的 
*/ 
int in_mid[10][10],vis[10];
int path[10],is_ok[10][10];
int ans,m;
void dfs(int now){
	if(now>=5){
		int tmp=0;
		for(int i=now-1;i>1;i--){
			if(is_ok[path[i]][path[i-1]]) tmp++;
		}
		if(tmp>=m) ans++;
	}
	for(int i=1;i<=9;i++){
		int tmp=in_mid[i][path[now-1]];
		if(!vis[tmp]||vis[i]) continue;
		vis[i]=1;
		path[now]=i;
		dfs(now+1);
		vis[i]=0;
	}
}
 
int main(){
	in_mid[1][3]=in_mid[3][1]=2;
	in_mid[4][6]=in_mid[6][4]=5;
	in_mid[7][9]=in_mid[9][7]=8;
	
	in_mid[1][7]=in_mid[7][1]=4;
	in_mid[2][8]=in_mid[8][2]=5;
	in_mid[3][9]=in_mid[9][3]=6;
	
	in_mid[1][9]=in_mid[9][1]=5;
	in_mid[3][7]=in_mid[7][3]=5;
	vis[0]=1;
	
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d %d",&u,&v);
		is_ok[u][v]=is_ok[v][u]=1;
	}	
	ans=0;
	dfs(1);
	printf("%d\n",ans);
	return 0;
}

一共可以形成多少中锁屏方案

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

const int maxn=10;
int in_mid[maxn][maxn];
int ans;
int path[maxn],vis[maxn];
/*
思路还是比较巧妙地,把不可能的情况列出来
也就是横向,纵向,斜向,这几种情况连续相连的情况
所以当我们检测的时候,只需要考虑[pre_choice][now_choice]
那么这个now_choice到底该不该选呢,就只需要考虑
我们是不是访问过他俩之间的这个非法访问点,只要访问过的话,
就没有什么问题了,很巧妙的思想,将复杂的问题简化了很多 
*/
void dfs(int now){
	if(now>=5)//说明长度已经大于4了 
		ans++;
	for(int i=1;i<=9;i++){
		int tmp=in_mid[path[now-1]][i];
		if(!vis[tmp]||vis[i]) continue;
		path[now]=i;
		vis[i]=1;
		dfs(now+1);
		vis[i]=0;
	}
}

int main(){
	memset(in_mid,0,sizeof(in_mid));
	in_mid[1][3]=in_mid[3][1]=2;
	in_mid[4][6]=in_mid[6][4]=5;
	in_mid[7][9]=in_mid[9][7]=8;
	
	in_mid[1][7]=in_mid[7][1]=4;
	in_mid[2][8]=in_mid[8][2]=5;
	in_mid[3][9]=in_mid[9][3]=6;
	
	in_mid[1][9]=in_mid[9][1]=5;
	in_mid[3][7]=in_mid[7][3]=5;
	ans=0;
	vis[0]=1;//把两个点之间相当于是添加了一个虚无的点 
	dfs(1);
	printf("%d\n",ans);	
	return 0;
}