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

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!” (引号不用输出)。
 

 

【题目大意】n个点,从1~n,每次只能走序号比之前序号大的,并且不能出现规定的顺序,求1~n的最短路径
 
【解题思路】
 
规定的顺序可以看作AC自动机上的字符串,从一个节点到另外一个节点
因为如果上面的路径没有办法到达的话,就说明下面的路径也没有办法到达
 
之后,用dp去求
dp[ i ] [ j ]  表示现在位置在第 i 个节点同时已经走到了AC自动机上的节点 j ,即已经经过了对应的路径
 
然后枚举状态转移
dp [ k ] [ t r i e [ j ] [ k ] ]   =   m i n ( d p [ k ] [ t r i e [ j ] [ k ] ] ,   d p [ i ] [ j ]   +   d i s ( i ,   k ) ) 
 
从点 i 走到点 k ,AC自动机的节点从 j 到 k ,距离增加 dis( i ,j )
 
 
【注意】
数组的类型定义错误,改了一个小时QAQ
本来应该定义成double ,结果定义成了 i n t ,nmd
 
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int     MAXN = 6000;
const int     MAXM = 600;
const double  INF  = 1e18;
int     trie[600][60],cnt;
int     fail[600],val[600],n,m,k;
double  dp[60][600];
struct node
{
    double x, y;
}a[60];
void insert(double *v)
{
    int now = 0;
    for (int i = 1; i <=k; i++)
    {
        int u = v[i];
        if (!trie[now][u])
        {
            trie[now][u] = ++cnt;
        }
        now = trie[now][u];
    }
    val[now]=1;
}
void get_fail()
{
    int now = 0;
    queue<int> q;
    for (int i = 1; i <=n; i++)
        if (trie[now][i])
        {
            fail[trie[now][i]] = 0;
            q.push(trie[now][i]);
        }
    while (!q.empty())
    {
        int u = q.front(); q.pop();
        if (val[fail[u]])
            val[u]=1;
        for (int i = 1; i <=n; i++)
        {
            int v = trie[u][i];
            if (v)
            {
                fail[v] = trie[fail[u]][i];
                q.push(v);
            }
            else
            {
                trie[u][i] = trie[fail[u]][i];
            }
        }
    }
    return;
}
double dis(int i, int j)
{
    double tmp = (a[i].x - a[j].x)*(a[i].x - a[j].x) + (a[i].y - a[j].y)*(a[i].y - a[j].y);
    return sqrt(tmp);
}
double v[600];
double solve()
{
    double minn = INF;
    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= cnt; j++)
            dp[i][j] = INF;
    dp[1][trie[0][1]] = 0;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j <= cnt; j++)
        {
            if (dp[i][j]<INF)
            {
                for (int k = i + 1; k <= n; k++)
                    if (!val[trie[j][k]])
                        dp[k][trie[j][k]] = min(dp[k][trie[j][k]], dp[i][j] + dis(i, k));
            }
        }
    }
    for(int i=0;i<=cnt;i++)
            minn = min(minn, dp[n][i]);
    return minn;
}
int main()
{
    while (scanf("%d%d", &n, &m) && n && m)
    {
        cnt = 0;
        memset(trie, 0, sizeof(trie));
        memset(val, 0, sizeof(val));
        memset(fail, 0, sizeof(fail));
        for (int i = 1; i <= n; i++)
            scanf("%lf%lf", &a[i].x, &a[i].y);
        while(m--)
        {

            scanf("%d", &k);
            for (int i = 1; i <= k; i++)
                scanf("%lf", &v[i]);
            insert(v);
        }
        get_fail();
        double ans = solve();
        if (ans >= INF)
            printf("Can not be reached!\n");
        else
            printf("%.2f\n", ans);
    }
    return 0;
}
View Code

 

 
 
 
 
posted @ 2019-08-11 18:47  rentu  阅读(297)  评论(0编辑  收藏  举报