【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这个点的。
现在,小明即想走最短的路尽快见到女朋友,又不想打破女朋友的规定,你能帮助小明解决这个问题吗?
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
接下来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; }