题意:
给出S个自动机,每个自动机有n个结点和m个输出结点;
每个结点有两个后继‘0‘和‘1‘,将当前走过的串末尾加那个字符,然后走到下一个结点;
每次从0号点,以一个空串出发,到了输出结点时可以选择输出当前串;
如果一个自动机x可以输出的所有串另一个自动机y也都可以输出,那么y是x的升级;
求最大升级序列;
S,n,m<=50;
题解:
这道题稍微考虑一下之后,发现难以处理的地方是自动机;
那先讨论一下别的地方;
begin
如果已知了所有自动机之间的升级关系之后,怎么求出答案呢?
可以将x升级到y表示为一条边x->y,那么答案就是这个图中最长路+1;
但是如果图中有环就会出现正权环时最长路没有意义;
考虑有环的话,环上的所有自动机一定等价!
那么上Tarjan强连通分量,缩一下点,转化成DAG搞搞搞。。。
太麻烦了!
两个相等的自动机形成了双向边,那干掉一条不就变成单向边了;
每次干掉从编号大连到编号小的那条边,剩下的边也一定可以将所有的等价自动机连接起来;
那实际上建图之后直接跑Floyd就ok了;
end
水的地方搞完了,该难的地方了;
怎么判断一个自动机能否包括另一个自动机;
考虑暴力乱搞:
生成所有子串显然不行,无穷的子串是无法枚举出来的;
每次走一步,另一个自动机跟着走,走到输出结点看看另一个能不能输出?
想法不错,但是走到什么程度呢?
在考场上的我当时是懵逼的,于是想——我走50步试试?
走一次肯定不太好使,那多走几次吧!
于是每次判断走10^6/s/s次,每次走50步;
这样的随机化算法跑了跑,感觉可以骗一点。。。然后就90啦!
实际上正解也没差多远;
正解的搜索相当于多了一个记忆化;
搜索的状态是可以表示为自动机x结点编号和自动机y编号,所以状态数只有2500;
判断所以可以到达的状态中是否有走到的自动机x结点可以输出,而y不可输出的情况;
这样就是O(n^2)处理一次询问,总共O(S^2*n^2)的复杂度;
代码:
#include<queue> #include<stdio.h> #include<string.h> #include<algorithm> #define N 550 #define M 50000 #define pr pair<int,int> using namespace std; int ch[M][2],is[M],tot; int S,f[N][N],root[N]; queue<pr>q; int hash[N][N]; bool judge(int a,int b) { int i,x,y; while(!q.empty()) q.pop(); q.push(pr(root[a],root[b])); hash[0][0]=1; memset(hash,0,sizeof(hash)); while(!q.empty()) { x=q.front().first,y=q.front().second; q.pop(); if(is[x]&&!is[y]) return 0; if(ch[x][0]-root[a]<0||ch[y][0]-root[b]<0||ch[x][1]-root[a]<0||ch[y][1]-root[b]<0) exit(0); if(!hash[ch[x][0]-root[a]][ch[y][0]-root[b]]) { hash[ch[x][0]-root[a]][ch[y][0]-root[b]]=1; q.push(pr(ch[x][0],ch[y][0])); } if(!hash[ch[x][1]-root[a]][ch[y][1]-root[b]]) { hash[ch[x][1]-root[a]][ch[y][1]-root[b]]=1; q.push(pr(ch[x][1],ch[y][1])); } } return 1; } int main() { int n,m,i,j,k,x,y,ans; scanf("%d",&S); for(i=1;i<=S;i++) { scanf("%d%d",&n,&m); root[i]=tot+1; tot+=n; for(j=1;j<=m;j++) { scanf("%d",&x); is[x+root[i]]=1; } for(x=root[i];x<=tot;x++) { scanf("%d%d",&ch[x][0],&ch[x][1]); ch[x][0]+=root[i]; ch[x][1]+=root[i]; } } memset(f,-0x3f,sizeof(f)); for(i=1;i<=S;i++) { for(j=1;j<i;j++) { if(judge(i,j)) f[i][j]=1; if(judge(j,i)) f[j][i]=1; if(f[i][j]==1&&f[j][i]==1) f[i][j]=-0x3f3f3f3f; } } for(k=1;k<=S;k++) { for(i=1;i<=S;i++) { for(j=1;j<=S;j++) { f[i][j]=max(f[i][j],f[i][k]+f[k][j]); } } } for(i=1,ans=0;i<=S;i++) { for(j=1;j<=S;j++) { ans=max(ans,f[i][j]); } } printf("%d\n",ans+1); return 0; }