You are given n magical numbers a 1, a 2, ..., a n, such that the length of each of these numbers is 20 digits.
You can move from the i th number to the j th number, if the number of common digits between a i and a j is exactly 17 digits.
The number of common digits between two numbers x and y is computed is follow:
Where countX i is the frequency of the i th digit in the number x, and countY i is the frequency of the i th digit in the number y.
You are given two integers s and e, your task is to find the minimum numbers of moves you need to do, in order to finish at number a e starting from number a s.
Input
The first line contains an integer T (1 ≤ T ≤ 250), where T is the number of test cases.
The first line of each test case contains three integers n, s, and e (1 ≤ n ≤ 250) (1 ≤ s, e ≤ n), where n is the number of magical numbers, s is the index of the number to start from it, and e is the index of the number to finish at it.
Then n lines follow, giving the magical numbers. All numbers consisting of digits, and with length of 20 digits. Leading zeros are allowed.
Output
For each test case, print a single line containing the minimum numbers of moves you need to do, in order to finish at number a e starting from number a s. If there is no answer, print -1.
Example
1
5 1 5
11111191111191111911
11181111111111818111
11811171817171181111
11111116161111611181
11751717818314111118
3
Note
In the first test case, you can move from a 1 to a 2, from a 2 to a 3, and from a 3 to a 5. So, the minimum number of moves is 3 moves.
题意:给你n个长度为20的串(字符串只会出现0-9的数字),你以第 s 的字符串为起点到达终点为 e 的字符串最短路径
两串字符中有17个数相同就说明两串字符间有一条边联系。即 ans += min(第一个数0-9出现的次数,第二个数0-9出现的次数) = 17的时候,两边建立联系
思路:建图,然后最短路跑一遍,Dijkstra 复杂度 O(n*n)
代码:
#include <iostream> #include <algorithm> #include <string.h> #include <cstdio> #include <string> #include <cmath> #include <vector> #include <stack> #include <queue> #include <stack> #include <list> #include <map> #include <set> //#include <unordered_map> #define Fbo friend bool operator < (node a, node b) #define mem(a, b) memset(a, b, sizeof(a)) #define FOR(a, b, c) for (int a = b; a <= c; a++) #define RFOR(a, b, c) for (int a = b; a >= c; a--) #define off ios::sync_with_stdio(0) #define sc(a) scanf("%d",&a) #define pr(a) printf("%d\n",a); #define SC(n,m) scanf("%d%d",&n,&m) bool check1(int a) { return (a & (a - 1)) == 0 ? true : false; } using namespace std; typedef pair<int, int> pii; typedef long long ll; const int INF = 0x3f3f3f3f;//1e10 const int mod = 1e9 + 7; const int Maxn = 1010; const int M = Maxn * 20; const double pi = acos(-1.0); const double eps = 1e-8; /* * 单源最短路径,Dijkstra算法,邻接矩阵形式,复杂度为O(n^2) * 可更改路径权类型,但是权值必须为非负 * Dijkstra算法寻找的是下一个离给定顶点(单源)最近的顶点。 */ int vis[Maxn];//用来标记0和1 表示这个点是否被选择过 int G[Maxn][Maxn];//邻接矩阵用来存储图的信息 int dis[Maxn];//记录任意一点到这个点的最近距离 char str[Maxn][Maxn]; int a[Maxn][Maxn]; int n, s, e, minl, minid, sum; void Dijkstra() { mem(vis, 0); sum = 0; for (int i = 1; i <= n; i++) { dis[i] = G[s][i]; //s是起点 } dis[s] = 0;//起点到起点距离为0 vis[s] = 1;//选择起点 /*查找到原集合的最短的边,循环n-1次*/ for (int i = 1; i <= n-1; i++) { minl = INF; minid = 0; for (int j = 1; j <= n; j++) { if (vis[j] == 0 && dis[j] < minl)//循环找出dis的最小边的节点 { minl = dis[j]; minid = j; } } vis[minid] = 1; //每并入一个点都要对原来的边进行修正,保证任意时刻源点到目标点的距离都是最短的。 for (int j = 1; j <= n; j++)//添入新点后更新最小距离 { if (!vis[j] && dis[j] > dis[minid] + G[minid][j]) dis[j] = dis[minid] + G[minid][j]; } } } int main(){ int t; sc(t); while (t--) { mem(dis, INF); mem(G, INF); mem(a, 0); scanf("%d%d%d", &n, &s, &e); FOR(i, 1, n) { scanf("%s", str[i]); for (int j = 0; j < 20; j++) { a[i][str[i][j] - '0']++; } } FOR(i, 1, n) { FOR(j, i + 1, n) { int ans = 0; for (int k = 0; k < 10; k++) { ans += min(a[i][k], a[j][k]); } if (ans == 17) { G[i][j] = G[j][i] = 1; } } } Dijkstra(); if (dis[e] == INF) printf("-1\n"); else pr(dis[e]); } return 0; }