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;
}


你可能感兴趣的:(编程,C++,c,算法,ACM)