您的位置:首页 > 其它

hdu 4511 AC自动机 + dp

2013-03-26 16:04 453 查看
题目大意:

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

  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

解题报告:这题初看没什么头续,看到了这些路径,然后又想看的数据范围,K很好,想到了Trie,然后又想到了字符串匹配,于是想到了AC自动机。

想到了AC自动机,这题就好做了,就是一个AC自动机DP嘛。

把题目给的非法路径构成一棵Trie树,然做跑一次AC自动机。

然后做DP

dp[i][j]代表在第i个点自动机状态在j的最小距离。

然后枚举可走的点去转移就行了。

自动机状态最多有5*100个。

总的DP状态有50*500,每一个转移是50,最后的复杂度是50*50*500

刚刚过题目。

PS:一次AC,很高兴。

#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()
{
freopen("4511.txt", "r", stdin);
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
[i]<ans)ans=dp
[i];
}

if(dblcmp(INF-ans)!=0)printf("%.2f\n",ans);
else puts("Can not be reached!");

}
return 0;
}


转自:http://hi.baidu.com/chenwenwen0210/item/ca0768d039b0f1d793a974c9

检讨:当时做这题的时候没有想到应该用ac自动机。换做当时经常做acm的时候应该能想到。

把题目给的非法路径构成一棵Trie树,然后做跑一次AC自动机。

这题就是一些基础的东西结合。算法本身不难。自己实现一下吧,不套模板。

这份代码用的静态trie较高效。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: