zoukankan      html  css  js  c++  java
  • HDU 4511 小明系列故事——女友的考验 ( Trie图 && DP )

    题意 :  给出编号从1 ~ n 的 n 个平面直角坐标系上的点,求从给出的第一个点出发到达最后一个点的最短路径,其中有两种限制,其一就是只能从编号小的点到达编号大的点,再者不能走接下来给出的 m 个限制路径,也就是其中有些路线无法走。

    分析 : 把问题抽象一下就是用编号 1 ~ n 构造一个字符串,使得字符串不包含 m 个给出的子串,且构造的代价是最小的 ( 两点间距就是代价,也就是路长 )。那如果做了很多有关在 Trie图 || AC自动机上DP的题目,那这道题就使用很套路的方法就可以了。先将给出的 m 个路线丢去构建 Trie 图,然后定义 DP[i][j] = 在编号为 i 的点 且 在 Trie 图上编号为 j 的节点状态下,最短的路径长度是多少,则可得转移方程

    DP[i+1][k] = min( DP[i+1][k], DP[i][j] + GetDis(i, k) ) 且 i+1 <= k <= n (保证编号从小到大)

    其中 GetDis(i, k) 是坐标点 i 和 k 的距离、而且 Trie[j].Next[k].flag == 0 即在 Trie 图上 j 状态到 k 状态合法,并不会使其包含非法路径

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<queue>
    #include<math.h>
    using namespace std;
    const int Max_Tot = 555;
    const int Letter  = 55;
    const double INF = 1e20;///不能使用 0x3f3f3f3f
    int n, m;
    pair<int, int> Point[55];
    struct Aho{
        struct StateTable{
            int Next[Letter];
            int fail, flag;
        }Node[Max_Tot];
        int Size;
        queue<int> que;
    
        inline void init(){
            while(!que.empty()) que.pop();
            memset(Node[0].Next, 0, sizeof(Node[0].Next));
            Node[0].fail = Node[0].flag = 0;
            Size = 1;
        }
    
        inline void insert(int *s, int len){
            int now = 0;
            for(int i=0; i<len; i++){
                int idx = s[i];
                if(!Node[now].Next[idx]){
                    memset(Node[Size].Next, 0, sizeof(Node[Size].Next));
                    Node[Size].fail = Node[Size].flag = 0;
                    Node[now].Next[idx] = Size++;
                }
                now = Node[now].Next[idx];
            }
            Node[now].flag = 1;
        }
    
        inline void BuildFail(){
            Node[0].fail = 0;
            for(int i=0; i<n; i++){
                if(Node[0].Next[i]){
                    Node[Node[0].Next[i]].fail = 0;
                    que.push(Node[0].Next[i]);
                }else Node[0].Next[i] = 0;///必定指向根节点
            }
            while(!que.empty()){
                int top = que.front(); que.pop();
                Node[top].flag |= Node[Node[top].fail].flag;///注意了!!
                for(int i=0; i<n; i++){
                    int &v = Node[top].Next[i];
                    if(v){
                        que.push(v);
                        Node[v].fail = Node[Node[top].fail].Next[i];
                    }else v = Node[Node[top].fail].Next[i];
                }
            }
        }
    }ac;
    
    
    double GetDis(pair<int,int> &st, pair<int,int> &en)
    {
        double x1 =  (double)st.first;
        double x2 =  (double)en.first;
        double y1 =  (double)st.second;
        double y2 =  (double)en.second;
        return sqrt( (double)(1.0*x1-x2)*(double)(1.0*x1-x2) +
                     (double)(1.0*y1-y2)*(double)(1.0*y1-y2));
    }
    
    double dp[55][555];
    inline void Solve()
    {
    
        for(int i=0; i<n; i++)
            for(int j=0; j<ac.Size; j++)
                dp[i][j] = INF;
    
        dp[0][ac.Node[0].Next[0]] = 0;
        for(int i=0; i<n-1; i++){
            for(int j=0; j<ac.Size; j++){
                if(dp[i][j] < INF){
                    for(int k=i+1; k<n; k++){
                        int newi = k;
                        int newj = ac.Node[j].Next[k];
                        if(!ac.Node[ newj ].flag){
                            dp[newi][newj] = min(dp[newi][newj],
                                                 dp[i][j]+GetDis(Point[i], Point[k]));
                        }
                    }
                }
            }
        }
    
        double ans = INF;
        for(int i=0; i<ac.Size; i++)
            if(dp[n-1][i] < INF)
                ans = min(ans, dp[n-1][i]);
    
        if(ans == INF) puts("Can not be reached!");
        else printf("%.2lf
    ", ans);
    }
    
    int main(void)
    {
        while(~scanf("%d %d", &n, &m)){
            if(n==0 && m==0) break;
            for(int i=0; i<n; i++)///这里我将编号 -1 了,也就是编号从 0 ~ n-1,所以下面操作都是按个编号来
                scanf("%d %d", &Point[i].first, &Point[i].second);
            int tmp[10];
            ac.init();
            while(m--){
                int k;
                scanf("%d", &k);
                for(int i=0; i<k; i++){
                    scanf("%d", &tmp[i]);
                    tmp[i]--;///因为编号是 0 ~ n-1
                }
                ac.insert(tmp, k);
            }ac.BuildFail();
            Solve();
        }
        return 0;
    }
    View Code
  • 相关阅读:
    【NOIP】提高组2015 运输计划
    【BZOJ】1635: [Usaco2007 Jan]Tallest Cow 最高的牛
    【51nod】1766 树上的最远点对
    【BZOJ】2054: 疯狂的馒头
    【SRM20】数学场
    【Luogu】P3930 SAC E#1
    【Luogu】P3927 SAC E#1
    【Luogu】 P3928 SAC E#1
    【Codeforces】868C. Qualification Rounds
    【CodeForces】866D. Buy Low Sell High
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/7667714.html
Copyright © 2011-2022 走看看