题目链接
题意
直角坐标系中n个点,限制一些走过的路径的顶点顺序,要求从1到n最短的距离。如:不能经过1 -> 2 -> 3,那么就要求走过的路径不能包含有1 -> 2 -> 3这部分,但是1 -> 3 或者1 -> 2都是可以的,这样的限制路径可能有多条。
思路
求1到n的最短路问题,把所有限制的路建立AC自动机,这里用dp解最短路问题,dp[i][j]表示从1到i的j状态的最短距离。如果tag[trie[j][k]] 为0,则表示不是危险节点,用dp[k][trie[j][k]] = min(dp[k][trie[j][k]], dp[i][j] + mp[i][k])转移。
注意tag[now] |= tag[fail[now]]的步骤,即如果2->3是不能走的,则1->2->3也是不能走的。

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
const double inf = 1e20;
const int MAXN = 60000 + 10, M = 50;
int trie[MAXN][M];
int tag[MAXN];
int fail[MAXN];
int L = 0, root;
int newnode() {
    for (int i = 0; i < M; i++) trie[L][i] = 0;
    tag[L++] = 0;
    return L - 1;
}
void init() {
    L = 0;
    root = newnode();
}
void insertWords(int a[], int k)
{
    int now = root;
    for (int i = 0; i < k; i++) {
        int next = a[i];
        if(!trie[now][next])
            trie[now][next] = newnode();
        now = trie[now][next];
    }
    tag[now]++;
}
void getFail()//一个节点的fail指针是指向 这个节点表示的字符串的最长后缀串的最后一个节点
{
    queue<int> q;
    for(int i = 0; i < M; i++) {
        if(trie[0][i]) {
            fail[trie[0][i]] = 0;
            q.push(trie[0][i]);
        }
    }
    while (!q.empty())
    {
        int now = q.front();
        q.pop();
        tag[now] |= tag[fail[now]];
        for (int i = 0; i < M; i++) {
            if(trie[now][i]) {
                fail[trie[now][i]] = trie[fail[now]][i];
                q.push(trie[now][i]);
            }
            else trie[now][i] = trie[fail[now]][i];
        }
    }
}
struct node {
    double x, y;
}p[55];
int n, m;
double mp[55][55];
double getdis(int i, int j) {
    return sqrt((p[i].x - p[j].x) * (p[i].x - p[j].x) + (p[i].y - p[j].y) * (p[i].y - p[j].y));
}
double dp[55][MAXN];
double solve() {
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j < L; j++)
            dp[i][j] = inf;
    }
    dp[1][trie[0][1]] = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < L; j++) {
            if(dp[i][j] < inf) {
                for (int k = i + 1; k <= n; k++) {
                    if(tag[trie[j][k]] == 0)
                    dp[k][trie[j][k]] = min(dp[k][trie[j][k]], dp[i][j] + mp[i][k]);
                }
            }
        }
    }
    double ans = inf;
    for (int i = 0; i < L; i++) {
        ans = min(ans, dp[n][i]);
    }
    return ans;
}
int main()
{
    while (scanf("%d%d", &n, &m), n + m) {
        init();
        for (int i = 1; i <= n; i++) {
            scanf("%lf%lf", &p[i].x, &p[i].y);
        }
        for (int i = 1; i <= n; i++) {
            mp[i][i] = 0;
            for (int j = i + 1; j <= n; j++)
                mp[j][i] = mp[i][j] = getdis(i, j);
        }
        int a[10];
        for (int i = 0; i < m; i++) {
            int k;
            scanf("%d", &k);
            for (int j = 0; j < k; j++)
                scanf("%d", &a[j]);
            insertWords(a, k);
        }
        getFail();
        double ans = solve();
        if(fabs(ans - inf) < 1e-3) printf("Can not be reached!\n");
        else printf("%.2f\n", ans);
    }
    return 0;
}