题意:
终于放寒假了,小明要和女朋友一起去看电影。这天,女朋友想给小明一个考验,在小明正准备出发的时候,女朋友告诉他,她在电影院等他,小明过来的路线必须满足给定的规则:
1、假设小明在的位置是1号点,女朋友在的位置是n号点,则他们之间有n-2个点可以走,小明每次走的时候只能走到比当前所在点编号大的位置;
2、小明来的时候不能按一定的顺序经过某些地方。比如,如果女朋友告诉小明不能经过1 -> 2 -> 3,那么就要求小明来的时候走过的路径不能包含有1 -> 2 -> 3这部分,但是1 -> 3 或者1 -> 2都是可以的,这样的限制路径可能有多条。
这让小明非常头痛,现在他把问题交给了你。
特别说明,如果1 2 3这三个点共线,但是小明是直接从1到3然后再从3继续,那么此种情况是不认为小明经过了2这个点的。
现在,小明即想走最短的路尽快见到女朋友,又不想打破女朋友的规定,你能帮助小明解决这个问题吗?
题解:
1、假设小明在的位置是1号点,女朋友在的位置是n号点,则他们之间有n-2个点可以走,小明每次走的时候只能走到比当前所在点编号大的位置;
2、小明来的时候不能按一定的顺序经过某些地方。比如,如果女朋友告诉小明不能经过1 -> 2 -> 3,那么就要求小明来的时候走过的路径不能包含有1 -> 2 -> 3这部分,但是1 -> 3 或者1 -> 2都是可以的,这样的限制路径可能有多条。
这让小明非常头痛,现在他把问题交给了你。
特别说明,如果1 2 3这三个点共线,但是小明是直接从1到3然后再从3继续,那么此种情况是不认为小明经过了2这个点的。
现在,小明即想走最短的路尽快见到女朋友,又不想打破女朋友的规定,你能帮助小明解决这个问题吗?
题解:
一些路径不能走,通过AC自动机得到状态转移。
之后dp[i][j] 表示在i点,状态在j的距离
网上看到一个floyed的做法,最后发现可以 cha掉,路径不能只看两个点的。如1->3 1->2->3 这样1是不能到3的。
坐标int相减会溢出int,最好直接用double.
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define L(i) i<<1
#define R(i) i<<1|1
//#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-9
#define maxn 10010
#define MOD 1000000007
const double INF = 1e20;
int n,m,a[11];
pair<int,int> p[110];
double dis(pair<int,int>a,pair<int,int>b)//坐标相减爆int
{
return sqrt((double)(1.0 * a.first - b.first) * (1.0 * a.first - b.first) + (double)(1.0 * a.second - b.second)*(1.0 * a.second - b.second));
}
double dp[55][1010];
struct Trie
{
int next[1010][55],fail[1010],en[1010];
int root,L;
void init()
{
L = 0;
root = newnode();
}
int newnode()
{
for(int i = 1; i <= n; i++)
next[L][i] = -1;
en[L++] = 0;
return L-1;
}
void Insert(int a[],int cnt)
{
int now = root;
for(int i = 0; i < cnt; i++)
{
if(next[now][a[i]] == -1)
next[now][a[i]] = newnode();
now = next[now][a[i]];
}
en[now] = 1;
}
void build()
{
queue<int> Q;
fail[root] = root;
for(int i = 1; i <= n; i++)
{
if(next[root][i] == -1)
next[root][i] = root;
else
{
fail[next[root][i]] = root;
Q.push(next[root][i]);
}
}
while(!Q.empty())
{
int now = Q.front();
Q.pop();
en[now] |= en[fail[now]];
for(int i = 1; i <= n; i++)
{
if(next[now][i] == -1)
next[now][i] = next[fail[now]][i];
else
{
fail[next[now][i]] = next[fail[now]][i];
Q.push(next[now][i]);
}
}
}
}
void solve()
{
for(int i = 1; i <= n; i++)
for(int j = 0; j < L; j++)
dp[i][j] = INF;
dp[1][next[root][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++)
{
int ss = next[j][k];
if(en[ss])
continue;
dp[k][ss] = min(dp[k][ss],dp[i][j]+dis(p[i],p[k]));
}
}
double ans = INF;
for(int i = 0; i < L; i++)
if(dp[n][i] < INF)
ans = min(ans,dp[n][i]);
if(ans == INF)
printf("Can not be reached!\n");
else
printf("%.2f\n",ans);
}
}ac;
int main()
{
int t,C = 1;
//scanf("%d",&t);
while(scanf("%d%d",&n,&m) && (n+m))
{
for(int i = 1; i <= n; i++)
scanf("%d%d",&p[i].first,&p[i].second);
ac.init();
int k;
while(m--)
{
scanf("%d",&k);
for(int i = 0; i < k; i++)
scanf("%d",&a[i]);
ac.Insert(a,k);
}
ac.build();
ac.solve();
}
return 0;
}