您的位置:首页 > 其它

HDU 4511 小明系列故事——女友的考验(自动机+DP)

2015-04-21 00:48 435 查看
Problem Description

  终于放寒假了,小明要和女朋友一起去看电影。这天,女朋友想给小明一个考验,在小明正准备出发的时候,女朋友告诉他,她在电影院等他,小明过来的路线必须满足给定的规则:

  1、假设小明在的位置是1号点,女朋友在的位置是n号点,则他们之间有n-2个点可以走,小明每次走的时候只能走到比当前所在点编号大的位置;

  2、小明来的时候不能按一定的顺序经过某些地方。比如,如果女朋友告诉小明不能经过1 -> 2 -> 3,那么就要求小明来的时候走过的路径不能包含有1 -> 2 -> 3这部分,但是1 -> 3 或者1 -> 2都是可以的,这样的限制路径可能有多条。

  这让小明非常头痛,现在他把问题交给了你。

  特别说明,如果1 2 3这三个点共线,但是小明是直接从1到3然后再从3继续,那么此种情况是不认为小明经过了2这个点的。

  现在,小明即想走最短的路尽快见到女朋友,又不想打破女朋友的规定,你能帮助小明解决这个问题吗?



Input

  输入包含多组样例,每组样例首先包含两个整数n和m,其中n代表有n个点,小明在1号点,女朋友在n号点,m代表小明的女朋友有m个要求;

  接下来n行每行输入2个整数x 和y(x和y均在int范围),代表这n个点的位置(点的编号从1到n);

  再接着是m个要求,每个要求2行,首先一行是一个k,表示这个要求和k个点有关,然后是顺序给出的k个点编号,代表小明不能走k1 -> k2 -> k3 ……-> ki这个顺序的路径;

  n 和 m等于0的时候输入结束。

  [Technical Specification]

  2 <= n <= 50

  1 <= m <= 100

  2 <= k <= 5



Output

  对于每个样例,如果存在满足要求的最短路径,请输出这个最短路径,结果保留两位小数;否则,请输出”Can not be reached!” (引号不用输出)。



Sample Input

3 1
1 1
2 1
3 1
2
1 2

2 1
0 0
1 1
2 
1 2

5 3
0 0
5 3
1 2
1 22
5 21
3
1 2 3
2 
4 5
2
1 5

0 0




Sample Output

2.00
Can not be reached!
21.65



状态也很好想,设dp[i][j]代表走到i节点,在自动机上状态为j时的最小花费。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<string>
#include<iostream>
#include<queue>
#include<cmath>
#include<map>
#include<stack>
#include<bitset>
using namespace std;
#define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define CLEAR( a , x ) memset ( a , x , sizeof a )
typedef long long LL;
typedef pair<int,int>pil;
const double INF = 1e20;
struct node{
    int x,y;
}e[110];
double dp[55][1100];
int n,m;
double dis(node l1,node l2)
{
    return sqrt((double)(1.0*l1.x-l2.x)*(1.0*l1.x-l2.x)+(double)(1.0*l1.y-l2.y)*(1.0*l1.y-l2.y));
}
struct AC{
     int next[1100][55];
     int fail[1100];
     int ed[1100];
     int root,L;

     int newnode()
     {
         for(int i=1;i<=n;i++)
            next[L][i]=-1;
         ed[L++]=0;
         return L-1;
     }
     void init()
     {
         L=0;
         root=newnode();
     }
     void Insert(char buf[])
     {
         int len=strlen(buf);
         int now=root;
         for(int i=0;i<len;i++)
         {
             int id=buf[i]-'0';
             if(next[now][id]==-1)
                next[now][id]=newnode();
             now=next[now][id];
         }
         ed[now]=1;
     }
     void Build_AC()
     {
         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();
             ed[now]|=ed[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;//开始时在1位置,所以节点不在自动机的root节点了
         for(int i=1;i<=n;i++)
         {
             for(int j=0;j<L;j++)
             {
                 if(dp[i][j]==INF) continue;
                 for(int k=1;k<=n;k++)
                 {
                     int x=next[j][k];
                     if(ed[x]) continue;
                     dp[k][x]=min(dp[k][x],dp[i][j]+dis(e[i],e[k]));
                 }
             }
         }
         double ans=INF;
         for(int i=0;i<L;i++)
            ans=min(ans,dp
[i]);
         if(ans==INF) puts("Can not be reached!");
         else printf("%.2f\n",ans);
     }
};
AC A;
int main()
{
    char str[10];int x,k;
    while(~scanf("%d%d",&n,&m))
    {
        if(n+m==0) break;
        A.init();
        REPF(i,1,n)
          scanf("%d%d",&e[i].x,&e[i].y);
        while(m--)
        {
            int l=0;
            scanf("%d",&k);
            for(int i=0;i<k;i++)
            {
                scanf("%d",&x);
                str[l++]=x+'0';
            }
            str[l]='\0';
            A.Insert(str);
        }
        A.Build_AC();
        A.solve();
    }
    return 0;
}

/*
3 1
1 1
2 1
3 1
2
1 2
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: