題目描述
輸入兩個DFA,判斷是否等價。
https://uva.onlinejudge.org/external/16/1671.pdf
輸入
第一行T 可以接受的字母表
第二行N 狀態數
接下去N行 每個狀態的情況 第一個表示是否爲接受狀態 後面T個是接受字母i可以轉移到的狀態 -1表示不轉移
輸出
等價輸出Yes,不等價輸出No
樣例輸入
2
3
1 -1 1
0 -1 2
0 -1 0
2
1 -1 1
0 -1 0
3
4
1 -1 -1 1
1 -1 -1 2
1 -1 -1 3
1 -1 -1 1
2
1 -1 -1 1
1 -1 -1 0
0
樣例輸出
Case #1: No
Case #2: Yes
思路
參考劉神的思路,求DFA2的補,然後與DFA1測試相交。如果不相交,當然是等價。
求補的思路就是把接受狀態全都取反。求相交的思路就是BFS,或者DFS。我用的是BFS,很快,複雜度O(n)。
一個重要的細節,我在這上面卡了很久,就是怎麼處理-1的情況。最後的解法是設一個孤島結點,孤島的所有轉移都指向自己,-1的情況就轉移到孤島,孤島自己不是接受狀態。這樣做的正確性:原DFA可以接受的狀態集沒有改變。因爲我只需要知道那些輸入可以接受,其餘的就是不接受的輸入。
但是如果不要孤島,怎麼直接處理-1的情況?
首先,如果遇到-1直接pass,那麼處理不了
DFA1: 1->2->3->4->5(ac)
DFA2: 1->2->3->4->5(ac)->6(ac)
或者
DFA2: 1->2->3->4->5(ac)->6->7-> ... ->n(ac)
而且,處理起來很麻煩,情況很多,所以還是加個孤島結點吧。
實現
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
const int maxn = 2010; // size of state
const int maxm = 30; // size of alphabet
int T; // T <= 26
int n1, n2; // n <= 2000
bool is_final1[maxn], is_final2[maxn];
int dfa1[maxn][maxm], dfa2[maxn][maxm];
bool init()
{
scanf("%d", &T);
if (T == 0)
return false;
scanf("%d", &n1);
for (int i = 0; i < n1; i++) {
int f1;
scanf("%d", &f1);
is_final1[i] = ((f1 == 1) ? true : false);
for (int j = 0; j < T; j++) {
scanf("%d", &dfa1[i][j]);
dfa1[n1][j] = n1;
if (dfa1[i][j] == -1) // 孤島狀態
dfa1[i][j] = n1;
}
}
is_final1[n1] = 0; // 孤島
scanf("%d", &n2);
for (int i = 0; i < n2; i++) {
int f2;
scanf("%d", &f2);
is_final2[i] = ((f2 == 1) ? true : false);
for (int j = 0; j < T; j++) {
scanf("%d", &dfa2[i][j]);
dfa2[n2][j] = n2;
if (dfa2[i][j] == -1) // 孤島狀態
dfa2[i][j] = n2;
}
}
is_final2[n2] = 0;
return true;
}
bool visit[maxn][maxn];
bool bfs(int a1[maxn][maxm], int a2[maxn][maxm], bool b1[maxn], bool b2[maxn])
{
memset(visit, false, sizeof visit);
queue<pair<int,int> > Q;
Q.push(make_pair(0, 0));
visit[0][0] = true;
while (!Q.empty()) {
pair<int,int> P = Q.front();
Q.pop();
if (b1[P.first] && !b2[P.second]) // 2求補集
return true; // 發現相交
for (int i = 0; i < T; i++) {
// bfs 遍歷未訪問轉移狀態
if (visit[a1[P.first][i]][a2[P.second][i]] == false) {
visit[a1[P.first][i]][a2[P.second][i]] = true;
Q.push(make_pair(a1[P.first][i], a2[P.second][i]));
}
}
}
return false; // 不相交
}
int main()
{
int kase = 0;
while (init()) {
printf("Case #%d: ", ++kase);
if (!bfs(dfa1, dfa2, is_final1, is_final2)
&& !bfs(dfa2, dfa1, is_final2, is_final1))
printf("Yes\n");
else
printf("No\n");
}
return 0;
}