本来想水一水AC自动机的,结果调了半天才调出来QAQ。
题意大概是有n个点,求1到n的最短路,且一些路径不能作为子串出现。
解法是把约束建一个AC自动机,在自动机上DP,由于是包含某个子串即非法,so构造fail往上跳时,如果当前和目标有一个为非法,另一个也应变成非法(见下图):
这里x号节点已经是非法,所以y号节点也会变为非法,即程序中。
然后DP时用f[i][j]记录原图第i个点在自动机上第j个节点的最短路径长度,每次对于当前i,j枚举下一个可以到达的点k,j进行更新。
最后ans=min(dp[n][0..节点数])
详细见代码:
#include <cstdio>
#include <algorithm>
#include <string>
#include <queue>
#include <cmath>
#include <cstring>
#define maxn 550
#define INF 0x7fffffffff
using namespace std;
int sz,n,m,k,ch[maxn][maxn],val[maxn],fail[maxn];
// AC自动机 是否可以走
string tmp;
double x[maxn],y[maxn],f[maxn][maxn];
// 表示第i个点在自动机第j个节点的路径长度
queue <int> q;
double dis(int a,int b)
{
return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));
}
void insert()//插入约束字符串
{
int u=0,n=tmp.length();
for (int i=0;i<n;i++)
{
int c=tmp[i];
if (!ch[u][c])
{
memset(ch[sz],0,sizeof(ch[sz]));
ch[u][c]=sz;
val[sz++]=0;
}
u=ch[u][c];
}
val[u]=1;
}
void get_fail()
{
for (int i=1;i<=n;i++)
if (ch[0][i])q.push(ch[0][i]);
while (!q.empty())
{
int u=q.front();q.pop();
for (int i=1;i<=n;i++)
if (ch[u][i])
{
int k;
for (k=fail[u];k&&!ch[k][i];k=fail[k]);
q.push(ch[u][i]);
fail[ch[u][i]]=ch[k][i];
val[ch[u][i]] | = val[ch[k][i]];//这一句很重要!很重要!!很重要!!!
}
else ch[u][i]=ch[fail[u]][i];//注释2:这一句等价于注释3
}
}
int main()
{
freopen("data.out","r",stdin);
freopen("wa.out","w",stdout);
while (scanf("%d%d",&n,&m))
{
memset(ch[0],0,sizeof(ch[0]));
memset(fail,0,sizeof(fail));
sz=1;
if (n==0&&m==0) break;
for (int i=1;i<=n;i++) scanf("%lf%lf",&x[i],&y[i]);
for (int i=1;i<=m;i++)
{
scanf("%d",&k);
tmp="";
int p;
for (int j=1;j<=k;j++) scanf("%d",&p),tmp+=p;
insert();
}
get_fail();
if (val[ch[0][1]])printf("Can not be reached!\n");//1作为起点不能到达无解
else
{
for (int i=1;i<=n;i++)
for (int j=0;j<sz;j++)
f[i][j]=INF;
f[1][ch[0][1]]=0;
for (int i=1;i<n;i++)
for (int j=0;j<sz;j++)
{
if (f[i][j]==INF) continue;
for (int k=i+1;k<=n;k++)
{
int jj=j;
while (jj&&!ch[jj][k]) jj=fail[jj];//注释3:等价于注释2
jj=ch[jj][k];
if (!val[jj]) f[k][jj]=min(f[k][jj],f[i][j]+dis(k,i));//合法则更新
}
}
double ans=INF;
for (int i=0;i<sz;i++)ans=min(ans,f[n][i]);
if (ans==INF) printf("Can not be reached!\n");
else printf("%.2f\n",ans);
}
}
return 0;
}