浙大第15届校赛 3361 Valid Pattern Lock (搜索)

Pattern lock security is generally used in Android handsets instead of a password. The pattern lock can be set by joining points on a 3 × 3 matrix in a chosen order. The points of the matrix are registered in a numbered order starting with 1 in the upper left corner and ending with 9 in the bottom right corner.

浙大第15届校赛 3361 Valid Pattern Lock (搜索)_第1张图片

A valid pattern has the following properties:

  • A pattern can be represented using the sequence of points which it's touching for the first time (in the same order of drawing the pattern). And we call those points as active points.
  • For every two consecutive points A and B in the pattern representation, if the line segment connecting A and B passes through some other points, these points must be in the sequence also and comes before A and B, otherwise the pattern will be invalid.
  • In the pattern representation we don't mention the same point more than once, even if the pattern will touch this point again through another valid segment, and each segment in the pattern must be going from a point to another point which the pattern didn't touch before and it might go through some points which already appeared in the pattern.

Now you are given n active points, you need to find the number of valid pattern locks formed from those active points.

Input

There are multiple test cases. The first line of input contains an integerT indicating the number of test cases. For each test case:

The first line contains an integer n (3 ≤n ≤ 9), indicating the number of active points. The second line containsn distinct integers a1, a2, …an (1 ≤ ai ≤ 9) which denotes the identifier of the active points.

Output

For each test case, print a line containing an integerm, indicating the number of valid pattern lock.

In the next m lines, each contains n integers, indicating an valid pattern lock sequence. The m sequences should be listed in lexicographical order.

Sample Input

1
3
1 2 3

Sample Output

4
1 2 3
2 1 3
2 3 1
3 2 1

手机上的图文锁是一个3*3的矩阵,其排列严格如图所示
一个合法的图文锁遵照下列的规格:
1) 这个图文锁可以使用点的序列来表示它的首次接触(绘制顺序的模式)。,我们称这些点积极点。
2)对于每两个连续点A和B,如果A和B的连线通过一些其他的点,那么这些点必须在序列中,且以A开头以B结尾,否则图文将是非法的。
3)图文中的每一条边必须从一个点到达另一个未接触过的点,它也可能经过一些早就经过了的点。
给你n个积极点,让你找出组成合法图文锁的个数,并且以词典序输出所有的序列

就拿题中给的数据来说,积极点是1,2,3    对于序列1->2->3这是合法的  序列1->3->2这是非法的,违反规格第二条
2->1->3合法   2->3->1非法   3->1->2非法   3->2->1合法   所以一共有四种合法序列
看到这个题的时候本人认为这应该是一种全排列题,但队友说不是,所以就搜索了。理由是他会超时,要是给出9个积极数那它就有9!种排列,而对于每一种排列又要去查他的合法性,而且每种排列可以经过走过的点,这又是搜索思想,并且想一想知道限制条件太多,所以果断搜索了。

当时做这个题的时候,我们队应该吃错药了,竟然忘了统计合法的数目,而是一边搜索一边输出,最后发现这苦逼的漏洞想改的时候时间都快截止了,所以很气愤。下面的解法是我们学长做的,他们的解法更值得我们学习,所以就贴他们的吧,当然我也想把最好的展现出来

输出的时候用printf,否则会超时
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
int F[10][10];
vector<string> vec; //vec容器用来存储所有的合法图文,这样避免了开辟数组的麻烦
int num[10],visit[10],cvisit[10];//num数组用来存积极数,visit存构造图文时已经用过的数,cvisit存可以用的数
int n,ans=0;//ans累计合法图文的数目
void init()
{   //定义一个F数组,两个数a,b紧邻表示可以直接到达,就设F[a][b]=0;
    //对于不能直接到达的就将其值设为必须经过的点
    memset(F,0,sizeof(F));
    F[1][3]=F[3][1]=2;
    F[1][9]=F[9][1]=5;
    F[1][7]=F[7][1]=4;
    F[2][8]=F[8][2]=5;
    F[3][7]=F[7][3]=5;
    F[3][9]=F[9][3]=6;
    F[4][6]=F[6][4]=5;
    F[7][9]=F[9][7]=8;
}
void DFS(int u,string s,int count)
{
    if(count==n)
    {
        vec.push_back(s);
        ans++;
        return;
    }
    for(int i=0;i<n;i++)
    {
        if(visit[num[i]]) //这个数用过一次了我们就不用把它保存到字符串中了
            continue;
        if(F[num[i]][num[u]]) //从num[i]到num[u]中间隔着一个数w的话,分情况讨论
        {
            if(!cvisit[F[num[i]][num[u]]])  //这个数w不是积极数的话那肯定不能用,返回
                continue;
            if(!visit[F[num[i]][num[u]]])//要是这个数w是积极数但它还没有被用过那就不能直接从num[i]连到num[u];
                continue;
        }
        string ss=s+char(num[i]+'0');
        visit[num[i]]=1;
        DFS(i,ss,count+1);
        visit[num[i]]=0;
    }
}
int main()
{
    int T;
    cin>>T;
    init();
    while(T--)
    {
        cin>>n;
        vec.clear();//以下五行清空重新赋值
        memset(num,0,sizeof(num));
        memset(visit,0,sizeof(visit));
        memset(cvisit,0,sizeof(cvisit));
        ans=0;
        for(int i=0;i<n;i++)
        {
            cin>>num[i];
            cvisit[num[i]]=1;
        }
        sort(num,num+n);//排序之后我们再去搜索的时候它就是按词典序存的,这一点很巧妙
        for(int i=0;i<n;i++)
        {
            string s="";
            s=s+char(num[i]+'0');//每次我们都以num[i]作为开头元素
            visit[num[i]]=1;//标记该点已用
            DFS(i,s,1);
            visit[num[i]]=0;//回溯重新来过找下一个
        }
        cout<<ans<<endl;
        for(int i=0;i<int(vec.size());i++)
        {
            string s=vec[i];
            printf("%c",s[0]);
            for(int j=1;j<n;j++)
                printf(" %c",s[j]);
            printf("\n");
        }
    }
    return 0;
} 
 

你可能感兴趣的:(浙大第15届校赛 3361 Valid Pattern Lock (搜索))