分析:参考别人的搞。
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