/* |
2013腾讯马拉松初赛第2场 |
1002 小明系列故事——女友的考验 |
|
Time Limit: 0.2 Seconds Memory Limit: 32768K |
|
终于放寒假了,小明要和女朋友一起去看电影。这天,女朋友想给小明一个考验,在小明正准备出发的时候,女朋友告诉他,她在电影院等他,小明过来的路线必须满足给定的规则: |
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 |
*/ |
/* |
解题报告: |
这题初看没什么头续,看到了这些路径,然后又想看的数据范围,K很好,想到了Trie,然后又想到了字符串匹配,于是想到了AC自动机。 |
想到了AC自动机,这题就好做了,就是一个AC自动机DP嘛。 |
把题目给的非法路径构成一棵Trie树,然做跑一次AC自动机。 |
然后做DP |
dp[i][j]代表在第i个点自动机状态在j的最小距离。 |
然后枚举可走的点去转移就行了。 |
自动机状态最多有5*100个。 |
总的DP状态有50*500,每一个转移是50,最后的复杂度是50*50*500 |
*/ |
#include<stdio.h> |
#include<math.h> |
#include<string.h> |
const int MAX = 1005; |
const int MAXSON = 50; |
struct { |
int id, next[MAXSON], fail; |
} node[1000000]; |
int n, tot; |
char mod[1000005]; |
int len[MAX]; |
int q[1000000]; |
void clr() { |
int i; |
tot++; |
for (i = 0; i < MAXSON; i++) |
node[tot].next[i] = 0; |
node[tot].id = node[tot].fail = 0; |
} |
void ins() { |
int tmp, i, n; |
int h = 0; |
scanf ( "%d" , &n); |
while (n--) { |
scanf ( "%d" , &tmp); |
tmp--; |
if (node[h].next[tmp] == 0) { |
clr(); |
node[h].next[tmp] = tot; |
} |
h = node[h].next[tmp]; |
} |
node[h].id++; |
} |
void get_fail() { |
int h = 0, i; |
int f = -1, r = 0; |
int tmp, fail, k; |
q[0] = 0; |
while (f != r) { |
tmp = q[++f]; |
if (node[node[tmp].fail].id > 0) //自动机添加一个往前走的东西。错在这里啊,好久没有用AC自动机了 |
node[tmp].id = 1; |
for (i = 0; i < MAXSON; i++) { |
if (node[tmp].next[i] == 0) { |
fail = node[tmp].fail; |
node[tmp].next[i] = node[fail].next[i]; |
continue ; |
} |
k = node[tmp].next[i]; |
fail = node[tmp].fail; |
if (node[fail].next[i] != k) |
node[k].fail = node[fail].next[i]; |
else |
node[k].fail = 0; |
q[++r] = k; |
} |
} |
} |
const double EPS = 1.0e-8; |
bool dblcmp( double x) { |
if ( fabs (x) < EPS) return 0; |
return x < 0 ? -1 : 1; |
} |
struct Point { |
double x, y; |
} p[55]; |
double disPP(Point a, Point b) { |
double dx = a.x - b.x; |
double dy = a.y - b.y; |
return sqrt (dx * dx + dy * dy); |
} |
const double INF = 1000000000.0 * 100000000.0; |
double dp[55][5 * 110]; |
int main() { |
int n, m; |
int i, j, k; |
while ( scanf ( "%d%d" , &n, &m) != EOF) { |
if (n == 0 && m == 0) break ; |
for (i = 1; i <= n; i++) { |
scanf ( "%lf%lf" , &p[i].x, &p[i].y); |
} |
tot = -1; |
clr(); |
while (m--) { |
ins(); |
} |
get_fail(); |
for (i = 0; i <= n; i++) { |
for (j = 0; j <= tot; j++) { |
dp[i][j] = INF; |
} |
} |
int h = node[0].next[0]; |
if (node[h].id > 0) { |
puts ( "Can not be reached!" ); |
continue ; |
} |
dp[1][h] = 0; |
double cost = 0; |
for (i = 1; i <= n; i++) { |
for (j = 0; j <= tot; j++) { |
if (dblcmp(dp[i][j] - INF) == 0) continue ; |
for (k = i + 1; k <= n; k++) { |
h = node[j].next[k - 1]; |
if (node[h].id) continue ; |
cost = disPP(p[i], p[k]) + dp[i][j]; |
if (cost < dp[k][h])dp[k][h] = cost; |
} |
} |
} |
double ans = INF; |
for (i = 0; i <= tot; i++) { |
if (dp[n][i] < ans)ans = dp[n][i]; |
} |
if (dblcmp(INF - ans) != 0) printf ( "%.2f\n" , ans); |
else puts ( "Can not be reached!" ); |
} |
return 0; |
} |
初级程序员
by: 我的程序员之路 发表于:2013-05-19 20:49:23 顶(0) | 踩(0) 回复
算法果然才是精髓!
回复评论