题目大意:
数据
T
T
T组,
有
n
n
n道材料,
m
m
m个评委,
每个评委会有2个要求,
(
a
,
x
)
,
(
b
,
y
)
(a,x),(b,y)
(a,x),(b,y)
x
,
y
x,y
x,y为材料编号,
a
,
b
a,b
a,b为做法,做法仅有满式或者汉式
对于一个评委被满足,至少有一个要求被满足
每道材料有且只有一道,即每道材料只能被一种做法使用,
问是否有一种方案能使得所有的评委都满足
对于每组数据,有输出
G
O
O
D
GOOD
GOOD,没有输出
B
A
D
BAD
BAD
n
≤
100
,
m
≤
1000
n≤100,m≤1000
n≤100,m≤1000
T
≤
50
T≤50
T≤50
分析:
每种材料分成汉式跟满式做法,
然后可以可以转化成
2
−
S
A
T
2-SAT
2−SAT的模型,
[
1
,
n
]
满
式
做
法
处
理
材
料
1
n
[1,n] 满式做法处理材料1~n
[1,n]满式做法处理材料1 n
[
n
+
1
,
2
∗
n
]
汉
式
做
法
处
理
材
料
1
n
[n+1,2*n] 汉式做法处理材料1~n
[n+1,2∗n]汉式做法处理材料1 n
[
2
∗
n
+
1
,
3
∗
n
]
不
用
满
式
做
法
处
理
材
料
1
n
[2*n+1,3*n] 不用满式做法处理材料1~n
[2∗n+1,3∗n]不用满式做法处理材料1 n
[
3
∗
n
+
1
,
4
∗
n
]
不
用
汉
式
做
法
处
理
材
料
1
n
[3*n+1,4*n] 不用汉式做法处理材料1~n
[3∗n+1,4∗n]不用汉式做法处理材料1 n
考虑建模,
(1)不选汉式做法与选满式做法间连双向边
(2)不选满式做法与选汉式做法间连双向边
(3)对于每个要求,连2条单向边:
①不做第一道菜->做第二道菜
②不做第二道菜->做第一道菜
然后就是经典的
2
−
S
A
T
2-SAT
2−SAT判定问题,
利用tarjan缩点以后,
如果
①同一道材料的2种做法在同一个SCC中
②同一种做法的做与不做在同一个SCC中
则是无解的
否则有解
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdlib>
#include <algorithm>
#define mt(x) memset(x, 0, sizeof(x))
#define N 105
using namespace std;
struct Node { int To, nxt; }e[2505];
int Belong[N*4], Instack[N*4], dfn[N*4], low[N*4], ls[N*4], T, n, m, cnt, cdp, total;
char AA[5], BB[5];
stack <int> st;
void Pre_Work()
{
mt(Instack); mt(Belong); mt(dfn); mt(low); mt(ls);
cnt = 0; cdp = 0; total = 0;
}
void Read_Work(int &x)
{
char c = getchar();
while (c != 'h' && c != 'm') c = getchar();
scanf("%d", &x);
if (c == 'h') x = x + n;
}
void Addedge(int u, int v)
{
e[++cnt].To = v, e[cnt].nxt = ls[u], ls[u] = cnt;
}
void Tarjan(int u)
{
dfn[u] = low[u] = ++cdp;
Instack[u] = 1;
st.push(u);
for (int i = ls[u]; i; i = e[i].nxt)
{
int v = e[i].To;
if (!dfn[v]) Tarjan(v), low[u] = min(low[u], low[v]);
else if (Instack[v]) low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u])
{
++total; int yzh;
while ("displaylzy")
{
yzh = st.top(); st.pop();
Belong[yzh] = total, Instack[yzh] = 0;
if (yzh == u) break;
}
}
}
// 1~n 满式做法处理材料1~n
// n+1~2*n 汉式做法处理材料1~n
// 2*n+1~3*n 不用满式做法处理材料1~n
// 3*n+1~4*n 不用汉式做法处理材料1~n
int main()
{
scanf("%d", &T);
while (T--)
{
scanf("%d %d", &n, &m);
Pre_Work();
for (int i = 1; i <= m; i++)
{
int AA, BB;
Read_Work(AA);
Read_Work(BB);
Addedge(AA + 2 * n, BB);
Addedge(BB + 2 * n, AA);
}
for (int i = 1; i <= n; i++)
{
Addedge(2 * n + i, n + i); Addedge(n + i, 2 * n + i);
Addedge(3 * n + i, i); Addedge(i, 3 * n + i);
}
for (int i = 1; i <= 4 * n; i++) if (!dfn[i]) Tarjan(i);
bool Check = 0;
for (int i = 1; i <= n; i++)
if (Belong[i] == Belong[n + i] || Belong[i] == Belong[2 * n + i] || Belong[n + i] == Belong[3 * n + i]) Check = 1;
if (Check) printf("BAD\n"); else printf("GOOD\n");
}
return 0;
}