HDU ACM 4511 小明系列故事——女友的考验->AC自动机+DP

分析:参考别人的搞。

1、AC自动机:

使用AC自动机来压缩路段,如禁掉的路段是1->2->3,那么插入字符串(123) ,注意点只有1~50,所以0~50用ASCII 压缩成字符串即可。

这样就能够完成禁止路段的在线状态转移。

2、DP部分:

两点之间的最短路。dp[i][j]表示在地点i,当前字符是j的状态。

初始化:fill(&dp0][0],&dp[maxn-1][maxp-1],inf),inf=1e12,memset不能用。

边界:首先找出出发点在Pool中的位置,s=root->next[0],如果s被禁,那么就不用算了。否则dp[0][s]=0;

方程:  dp[k][t]=min(dp[k][t],dp[i][j]+dist(i,k))

答案:min(dp[n-1][0..cnt])

其中for(0...i...n-1),for(i+1...k....n) ,枚举任意两个点。

然后就是中间多出的AC自动机的状态压缩for(0....j....cnt),负责打出所有的路径转移状态。

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

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

预处理出任意两点的距离,然后可以顺着trie树中的节点走,不能走到不合法的地方,另开一维表示走到了哪里,依次来更新。

看了别人的思路,好像可以用AC自动机+最短路做,好像还可以用最短路加上标记做。

另外用库函数pow就TLE。

#include<iostream>
#include<string.h>
#include<queue>
#include<cmath>
#include<algorithm>
using namespace std;

#define MAX_N 55      //节点数
#define MAX_P 1000     //状态机状态数
#define INF 1e12       //最大值

struct Point           //点
{
	double x,y;
};

struct AC_node       //AC自动自节点
{
	AC_node* next[MAX_N],*fail;   //fail为失败指针
	int cnt;      //以该节点结束的字符串个数
};

Point p[MAX_N];
double dp[MAX_N][MAX_P];  //dp[i][j]代表在第i个点自动机状态在j的最小距离
AC_node Pool[MAX_P];     //AC自动机节点
AC_node* root,*sz;    //AC自动机根节点指针及下一个未使用节点指针

AC_node* new_AC_node()  //获得一个节点
{
	AC_node* ans;

	ans=sz++;
	memset(ans->next,NULL,sizeof(ans->next));
	ans->fail=NULL;
	ans->cnt=0;
	return ans;
}

void Init_AC()       //初始化AC自动机相关
{
	sz=Pool;
	root=new_AC_node();
}

void AC_Insert(string s) //AC自动机中插入一个字符串
{
	AC_node* pos;
	int i,c;

	pos=root;
	for(i=0;i<s.size();i++)
	{
		c=s[i];
		if(!pos->next[c])
			pos->next[c]=new_AC_node();
		pos=pos->next[c];
	}
	pos->cnt++;
}

void Build_AC() //建立AC自动机
{
	queue<AC_node*> q;
	int c;
	AC_node* x;

	for(c=0;c<MAX_N;c++)
		if(root->next[c])
		{
			root->next[c]->fail=root;
			q.push(root->next[c]);
		}
		else root->next[c]=root;
	while(!q.empty())                      //建立自动机的方式方便后序DP的处理
	{
		x=q.front();
		q.pop();
		for(c=0;c<MAX_N;c++)
			if(x->next[c])
			{
		    	x->next[c]->fail=x->fail->next[c];
		    	x->next[c]->cnt+=x->fail->next[c]->cnt;
		    	q.push(x->next[c]);
			}
	     	else
		    	x->next[c]=x->fail->next[c];
	}
}

double dis(const Point& a,const Point& b)
{
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

int main()
{
	int n,m,i,j,k,tmp;
	int s,cnt,t;
	AC_node* pos;
	double ans;

	while(scanf("%d%d",&n,&m)==2 && n)
	{
		Init_AC();
		for(i=0;i<n;i++)
			scanf("%lf%lf",&p[i].x,&p[i].y);
		for(i=1;i<=m;i++)
		{
			scanf("%d",&k);
			string ss;
			for(j=1;j<=k;j++)
			{
				scanf("%d",&tmp);
				ss+=tmp-1;
			}
			AC_Insert(ss);
		}
		Build_AC();

    	s=root->next[0]-Pool;  //找到出发点的位置
    	if(root->next[0]->cnt)  //第一个就被标记,显然不能
    		puts("Can not be reached!");
    	else
		{
    		cnt=sz-Pool;
    		fill(&dp[0][0],&dp[MAX_N-1][MAX_P-1],INF);  //初始化dp为最大值
    		dp[0][s]=0;

    		for(i=0;i<n-1;i++)
	    		for(j=0;j<cnt;j++)
				{
	    			pos=Pool+j;
	    			for(k=i+1;k<n;k++)
					{
		    			if(pos->next[k]->cnt) continue;
		    			t=pos->next[k]-Pool;
		    			dp[k][t]=dp[k][t]<dp[i][j]+dis(p[i],p[k])?dp[k][t]:dp[i][j]+dis(p[i],p[k]);
					}
				}
	    	ans=INF;
	    	for(i=0;i<cnt;i++)
	    		ans=ans<dp[n-1][i]?ans:dp[n-1][i];
	    	if(ans==INF)
	    		puts("Can not be reached!");
	    	else
	    		printf("%.2lf\n",ans);
		}
	}
    return 0;
}
时间: 2024-10-10 23:15:30

HDU ACM 4511 小明系列故事——女友的考验->AC自动机+DP的相关文章

HDU - 4511 小明系列故事――女友的考验(AC自动机+DP)

Description 终于放寒假了,小明要和女朋友一起去看电影.这天,女朋友想给小明一个考验,在小明正准备出发的时候,女朋友告诉他,她在电影院等他,小明过来的路线必须满足给定的规则: 1.假设小明在的位置是1号点,女朋友在的位置是n号点,则他们之间有n-2个点可以走,小明每次走的时候只能走到比当前所在点编号大的位置: 2.小明来的时候不能按一定的顺序经过某些地方.比如,如果女朋友告诉小明不能经过1 -> 2 -> 3,那么就要求小明来的时候走过的路径不能包含有1 -> 2 ->

hdu4511---小明系列故事——女友的考验(AC自动机+dp)

小明系列故事--女友的考验 Time Limit: 500/200 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others) Total Submission(s): 863 Accepted Submission(s): 192 Problem Description 终于放寒假了,小明要和女朋友一起去看电影.这天,女朋友想给小明一个考验,在小明正准备出发的时候,女朋友告诉他,她在电影院等他,小明过来的路线必须满足给定的规则: 1.假

hdu4511小明系列故事——女友的考验(ac自动机+最短路)

链接 预处理出来任意两点的距离,然后可以顺着trie树中的节点走,不能走到不合法的地方,另开一维表示走到了哪里,依次来更新. 注意判断一下起点是不是合法. 1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmat

hdu 4511 小明系列故事——女友的考验

小明系列故事——女友的考验 Time Limit: 500/200 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Submission(s): 822    Accepted Submission(s): 176 Problem Description 终于放寒假了,小明要和女朋友一起去看电影.这天,女朋友想给小明一个考验,在小明正准备出发的时候,女朋友告诉他,她在电影院等他,小明过来的路线必须满足给定的规则

HDU 4511 小明系列故事——女友的考验 (AC自动机+DP)

小明系列故事--女友的考验 Time Limit: 500/200 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Submission(s): 1734    Accepted Submission(s): 466 Problem Description 终于放寒假了,小明要和女朋友一起去看电影.这天,女朋友想给小明一个考验,在小明正准备出发的时候,女朋友告诉他,她在电影院等他,小明过来的路线必须满足给定的规

AC自动机 + 二维最短路 HDU 4511 小明系列故事――女友的考验

这个题还是比较好想的. 首先将所有不可行方案建立AC自动机,然后跑最短路. 首先将小明放在(sta = 0,pos = 0)处,sta表示AC自动机上点的编号,pos表示坐标点的编号. 根据pos枚举下一次可以到达的地方[pos+1,n],然后sta在自动机上移动,如果某一步会使sta位于有标记的节点,那么这一步是不可行. #include <iostream> #include<time.h> #include<stdio.h> #include<string.

HDU 4511 小明系列故事——女友的考验 (AC自动机 + DP)题解

题意:从 1 走到 n,要求所走路径不能出现给定的路径,求最短路 思路:因为要求不能出现给定路径,那么我可以求助ac自动机完成判断. 我们可以在build的时候标记哪些路径不能出现,显然下面这种表示后缀不能出现,那么他也不能出现 if(node[node[u].fail].cnt && u) node[u].cnt = 1; //都不能取 然后再把图建完整.因为如果一个路径不在Trie中有两种情况,一种是他可能是某个不能走的串的前缀,那么我就重新指向这个不能走的串,比如Trie中只有AT,

【HDU 4511】小明系列故事——女友的考验(AC自动机+DP)

Problem Description 终于放寒假了,小明要和女朋友一起去看电影.这天,女朋友想给小明一个考验,在小明正准备出发的时候,女朋友告诉他,她在电影院等他,小明过来的路线必须满足给定的规则: 1.假设小明在的位置是1号点,女朋友在的位置是n号点,则他们之间有n-2个点可以走,小明每次走的时候只能走到比当前所在点编号大的位置: 2.小明来的时候不能按一定的顺序经过某些地方.比如,如果女朋友告诉小明不能经过1 -> 2 -> 3,那么就要求小明来的时候走过的路径不能包含有1 ->

HDU ACM 4506 小明系列故事——师兄帮帮忙 -&gt;简单快速幂

分析:t单位时间后i的位置将变化到(i+t)%n位置上,下标i从0开始,之后快速幂完成. #include<iostream> using namespace std; __int64 f_pow(__int64 a,__int64 b,__int64 mod) { __int64 s=1; while(b>0) { if(b&1) s=s*a%mod; a=a*a%mod; b>>=1; } return s; } int main() { int T; __int