分析:参考别人的搞。
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;
}
HDU ACM 4511 小明系列故事——女友的考验->AC自动机+DP
原文地址:http://blog.csdn.net/a809146548/article/details/46403075