HDU4511

来源:互联网 发布:sql镜像服务器 编辑:程序博客网 时间:2024/06/05 02:09

本来想水一水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 0x7fffffffffusing 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;}
0 0
原创粉丝点击