hdu 4511 AC自动机 + dp

题目大意:

 终于放寒假了,小明要和女朋友一起去看电影。这天,女朋友想给小明一个考验,在小明正准备出发的时候,女朋友告诉他,她在电影院等他,小明过来的路线必须满足给定的规则:
  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[n][i]<ans)ans=dp[n][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较高效。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值