Hopcroft DFA 最小化算法

复杂度 \(O(n\log n |\Sigma|)\) 非常优秀。

先存个板子。NOI 之后再讲解。

#include <cstdio>
#include <cassert>
#include <vector>
#include <algorithm>
using namespace std;
int read(){
	char c=getchar();int x=0;
	while(c<48||c>57) c=getchar();
	do x=(x<<1)+(x<<3)+(c^48),c=getchar();
	while(c>=48&&c<=57);
	return x;
}
const int N=500003;
vector<int> vec[N][10],eq[N],ele[N];
int bel[N],sz[N];
int n,m,cnt;
int que[N],tl;
int stk[N],tp;
bool tag[N],ed[N],ava[N];
int tr[N][10];
bool check(){
	for(int i=1;i<=cnt;++i){
		int c=0;
		for(int t:eq[i]) if(bel[t]==i) ++c;
		if(c!=sz[i]) return 0;
	}
	return 1;
}
int main(){
	freopen("hopcroft.in","r",stdin);
	freopen("hopcroft.out","w",stdout);
	n=read();m=read();int st=read();//点数,边数,起点
	cnt=2;
	for(int i=1;i<=n;++i) ava[i]=read();//接受节点
	for(int i=1;i<=m;++i){
		int u=read(),v=read(),c=read();
		vec[v][c].emplace_back(u);
	}
	for(int i=1;i<=n;++i)
		if(ava[i]) eq[1].emplace_back(i),bel[i]=1,++sz[1];
		else eq[2].emplace_back(i),bel[i]=2,++sz[2];
	assert(check());
	que[tl=1]=1;ed[1]=1;
	for(int pos=1;pos<=tl;++pos){
		int x=que[pos];
		vector<int> tmp;
		for(int p:eq[x])
			if(bel[p]==x) tmp.emplace_back(p);
		swap(eq[x],tmp);
		for(int c=0;c<10;++c){
			for(int p:eq[x])
				for(int q:vec[p][c]){
					if(ele[bel[q]].empty()) stk[++tp]=bel[q];
					ele[bel[q]].emplace_back(q);tag[q]=1;
				}
			while(tp){
				int y=stk[tp--];
				if(int(ele[y].size())<sz[y]){
					ed[++cnt]=ed[y];
					if((int(ele[y].size())<<1)<=sz[y]){
						for(int p:ele[y]) eq[bel[p]=cnt].emplace_back(p);
						sz[cnt]=ele[y].size();
						sz[y]-=ele[y].size();
					}
					else{
						vector<int> tmpy;
						sz[y]=0;
						for(int p:eq[y])
							if(bel[p]==y){
								if(tag[p]) tmpy.emplace_back(p),++sz[y];
								else eq[bel[p]=cnt].emplace_back(p),++sz[cnt];
							}
						swap(eq[y],tmpy);
					}
					que[++tl]=cnt;
				}
				for(int p:ele[y]) tag[p]=0;
				ele[y].clear();
				assert(check());
			}
		}
	}
	printf("%d\n",cnt);//点数
	printf("%d\n",bel[st]);//起点
	for(int i=1;i<=cnt;++i) printf("%d ",ed[i]);//接受节点
	putchar('\n');
	for(int i=1;i<=n;++i)
		for(int c=0;c<10;++c)
			for(int p:vec[i][c]) tr[bel[p]][c]=bel[i];
	for(int i=1;i<=cnt;++i){
		putchar('{');
		for(int c=0;c<10;++c)
			printf("%d%c",tr[i][c],",}"[c==9]);
		putchar(',');
		putchar('\n');
	}
	return 0;
}
posted @ 2023-07-12 19:42  yyyyxh  阅读(437)  评论(1编辑  收藏  举报