【BZOJ-1194】潘多拉的盒子 拓扑排序 + DP
1194: [HNOI2006]潘多拉的盒子
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 456 Solved: 215
[Submit][Status][Discuss]
Description
Input
第一行是一个正整数S,表示宝盒上咒语机的个数,(1≤S≤50)。文件以下分为S块,每一块描述一个咒语机,按照咒语机0,咒语机1„„咒语机S-1的顺序描述。每一块的格式如下。 一块的第一行有两个正整数n,m。分别表示该咒语机中元件的个数、咒语源输出元的个数(1≤m≤n≤50)。 接下来一行有m个数,表示m个咒语源输出元的标号(都在0到n-1之间)。接下来有n行,每一行两个数。第i行(0≤i≤n-1)的两个数表示pi,0和pi,1(当然,都在0到n-1之间)。
Output
第一行有一个正整数t,表示最长升级序列的长度。
Sample Input
4
1 1
0
0 0
2 1
0
1 1
0 0
3 1
0
1 1
2 2
0 0
4 1
0
1 1
2 2
3 3
0 0
1 1
0
0 0
2 1
0
1 1
0 0
3 1
0
1 1
2 2
0 0
4 1
0
1 1
2 2
3 3
0 0
Sample Output
3
HINT
Source
Solution
这道题还是比较有趣的..如果能够得到包含关系,可以直接拓扑排序dp求解最长链。
然后如何判断是否包含?需要利用自动机的相关性质。
考虑dp,$f[x][y]$表示自动机1和自动机2分别对应状态$x$和$y$,然后枚举转移更新$f[x'][y']$。
考虑什么情况不满足包含,即$f[x][y]=1$且$end[x]=1$且$end[y]=0$表示不包含,否则一定包含。
所以只要暴力的去枚举两两自动机跑即可,主要的思想就是去尝试构造01串使得一个能匹配另一个不能。
Code
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } int T,N,M; struct DFANode{ int son[55][2],end[55]; }DFA[55],A,B; bool f[55][55],flag; inline void Run(int x,int y) { if (!x || !y || f[x][y] || !flag) return; f[x][y]=1; if (!A.end[x] && B.end[y]) flag=0; for (int i=0; i<=1; i++) Run(A.son[x][i],B.son[y][i]); } inline bool Check(DFANode x,DFANode y) { memset(f,0,sizeof(f)); A=x,B=y; flag=1; Run(1,1); return flag; } struct GraphNode{ struct EdgeNode{ int next,to; }edge[55*55]; int head[55],cnt; inline void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;} inline void InsertEdge(int u,int v) {AddEdge(u,v);} }G1,G2; int visit[55],stack[55],top,dfn[55],low[55],dfsn; int belong[55],size[55],scc; inline void Tarjan(int x) { visit[x]=1; stack[++top]=x; dfn[x]=low[x]=++dfsn; for (int i=G1.head[x]; i; i=G1.edge[i].next) if (!dfn[G1.edge[i].to]) Tarjan(G1.edge[i].to),low[x]=min(low[x],low[G1.edge[i].to]); else if (visit[G1.edge[i].to]) low[x]=min(dfn[G1.edge[i].to],low[x]); if (dfn[x]==low[x]) { int tops=0; scc++; while (tops!=x) { tops=stack[top--]; belong[tops]=scc,visit[tops]=0,size[scc]++; } } } inline void Tarjan() {for (int i=1; i<=T; i++) if (!dfn[i]) Tarjan(i);} int mp[55][55],d[55]; inline void Rebuild() { for (int x=1; x<=T; x++) for (int i=G1.head[x]; i; i=G1.edge[i].next) if (belong[x]!=belong[G1.edge[i].to]) G2.InsertEdge(belong[x],belong[G1.edge[i].to]),d[belong[G1.edge[i].to]]++; } queue<int>q; int dp[55]; inline void Toposort() { for (int i=1; i<=scc; i++) if (!d[i]) q.push(i); for (int i=1; i<=scc; i++) dp[i]=size[i]; while (!q.empty()) { int now=q.front(); q.pop(); for (int i=G2.head[now]; i; i=G2.edge[i].next) { dp[G2.edge[i].to]=max(dp[G2.edge[i].to],dp[now]+size[G2.edge[i].to]); if (!--d[G2.edge[i].to]) q.push(G2.edge[i].to); } } } int main() { T=read(); for (int i=1; i<=T; i++) { N=read(),M=read(); for (int x,j=1; j<=M; j++) x=read()+1,DFA[i].end[x]=1; for (int j=1; j<=N; j++) DFA[i].son[j][0]=read()+1,DFA[i].son[j][1]=read()+1; } for (int i=1; i<=T; i++) for (int j=1; j<=T; j++) if (i!=j && Check(DFA[i],DFA[j])) G1.InsertEdge(i,j); Tarjan(); Rebuild(); Toposort(); int ans=0; for (int i=1; i<=scc; i++) ans=max(ans,dp[i]); printf("%d\n",ans); return 0; }
——It's a lonely path. Don't make it any lonelier than it has to be.