Luogu P4171 [JSOI2010]滿漢全席(2-SAT)
題意
題目描述
滿漢全席是中國最豐盛的宴客菜餚,有許多種不同的材料透過滿族或是漢族料理方式,呈現在數量繁多的菜色之中。由於菜色眾多而繁雜,只有極少數博學多聞技藝高超的廚師能夠做出滿漢全席,而能夠烹飪出經過專家認證的滿漢全席,也是中國廚師最大的榮譽之一。世界滿漢全席協會是由能料理滿漢全席的專家廚師們所組成,而他們之間還細分為許多不同等級的廚師。
為了招收新進的廚師進入世界滿漢全席協會,將於近日舉辦滿漢全席大賽,協會派遣許多會員當作評審員,為的就是要在參賽的廚師之中,找到滿料理界的明日之星。
大會的規則如下:每位參賽的選手可以得到\(n\)種材料,選手可以自由選擇用滿式或是漢料理將材料當成菜餚。
大會的評審制度是:共有\(m\)位評審員分別把關。每一位評審員對於滿漢全席有各自獨特的見解,但基本見解是,要有兩樣菜色作為滿漢全席的標誌。如某評審認為,如果沒有漢式東坡肉跟滿式的涮羊肉鍋,就不能算是滿漢全席。但避免過於有主見的稽核,大會規定一個評審員除非是在認為必備的兩樣菜色都沒有做出來的狀況下,才能淘汰一位選手,否則不能淘汰一位參賽者。
換句話說,只要參賽者能在這兩種材料的做法中,其中一個符合評審的喜好即可通過該評審的審查。如材料有豬肉,羊肉和牛肉時,有四位評審員的喜好如下表:
評審一 | 評審二 | 評審三 | 評審四 |
---|---|---|---|
滿式牛肉 | 滿式豬肉 | 漢式牛肉 | 漢式牛肉 |
漢式豬肉 | 滿式羊肉 | 漢式豬肉 | 滿式羊肉 |
如參賽者甲做出滿式豬肉,滿式羊肉和滿式牛料理,他將無法滿足評審三的要求,無法通過評審。而參賽者乙做出漢式豬肉,滿式羊肉和滿式牛料理,就可以滿足所有評審的要求。
但大會後來發現,在這樣的制度下如果材料選擇跟派出的評審員沒有特別安排好的話,所有的參賽者最多隻能通過部分評審員的審查而不是全部,所以可能會發生沒有人通過考核的情形。
如有四個評審員喜好如下表時,則不論參賽者採取什麼樣的做法,都不可能通過所有評審的考核:
| 評審一 | 評審二 | 評審三 | 評審四 |
| 滿式羊肉 | 滿式豬肉 | 漢式羊肉 | 漢式羊肉 |
| 漢式豬肉 | 滿式羊肉 | 漢式豬肉 | 滿式豬肉 |
所以大會希望有人能寫一個程式來判斷,所選出的\(m\)位評審,會不會發生沒有人能通過考核的窘境,以便協會組織合適的評審團。
輸入輸出格式
輸入格式:
第一行包含一個數字\(K\),代表測試檔案包含了\(K\)組資料。
每一組測試資料的第一行包含兩個數字\(n\)跟\(m\)(\(n\leq 100,m\leq 1000\)),代表有\(n\)種材料,\(m\)位評審員。
為方便起見,材料捨棄中文名稱而給予編號,編號分別從\(1\)到\(n\)。
接下來的\(m\)行,每行都代表對應的評審員所擁有的兩個喜好,每個喜好由一個英文字母跟一個數字代表,如\(m1\)代表這個評審喜歡第\(1\)個材料透過滿料理做出來的菜,而\(h2\)代表這個評審員喜歡第\(2\)個材料透過漢料理做出來的菜。
每個測試檔案不會有超過\(50\)組測試資料。
輸出格式:
每筆測試資料輸出一行,如果不會發生沒有人能通過考核的窘境,輸出GOOD
;否則輸出BAD
(大寫字母)。
輸入輸出樣例
輸入樣例#1:
2
3 4
m3 h1
m1 m2
h1 h3
h3 m2
2 4
h1 m2
m2 m1
h1 h2
m1 h2
輸出樣例#1:
GOOD
BAD
思路
每道菜是滿菜還是漢菜,就相當於每個布林變數是真還是假,所以這其實就是一道\(2-SAT\)問題板子題(實際上比板子還板),只需要判斷有無解即可,那麼就\(Tarjan\)縮點之後查詢每對點的縮點編號就好了,關於\(2-SAT\)的學習可以檢視這篇文章:Luogu P4782 【模板】2-SAT 問題(2-SAT)。
AC程式碼
#include<bits/stdc++.h>
using namespace std;
const int MAXN=205,MAXM=2005;
int T,n,m,tot,dfn[MAXN],low[MAXN];
int cnt,top[MAXN],to[MAXM],nex[MAXM];
int js,bel[MAXN];
bool ans,vis[MAXN];
stack<int>S;
int read()
{
int re=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
return re;
}
char readc()
{
char ch=getchar();
while(!isalpha(ch)) ch=getchar();
return ch;
}
void add_edge(int x,int y){to[++cnt]=y,nex[cnt]=top[x],top[x]=cnt;}
void tarjan(int now)
{
dfn[now]=low[now]=++tot,vis[now]=true;
S.push(now);
for(int i=top[now];i;i=nex[i])
if(!dfn[to[i]]) tarjan(to[i]),low[now]=min(low[now],low[to[i]]);
else if(vis[to[i]]) low[now]=min(low[now],dfn[to[i]]);
if(dfn[now]==low[now])
{
bel[now]=++js,vis[now]=false;
while(S.top()!=now) bel[S.top()]=js,vis[S.top()]=false,S.pop();
S.pop();
}
}
int main()
{
T=read();
while(T--)
{
n=read(),m=read(),cnt=tot=js=0,ans=false;
memset(top,0,sizeof top);
memset(dfn,0,sizeof dfn);
memset(low,0,sizeof low);
while(m--)
{
char xx=readc();int x=read();
char yy=readc();int y=read();
if(xx=='h'&&yy=='h') add_edge(x+n,y),add_edge(y+n,x);
else if(xx=='h'&&yy=='m') add_edge(x+n,y+n),add_edge(y,x);
else if(xx=='m'&&yy=='h') add_edge(x,y),add_edge(y+n,x+n);
else if(xx=='m'&&yy=='m') add_edge(x,y+n),add_edge(y,x+n);
}
for(int i=1;i<=(n<<1);i++) if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;i++)
if(bel[i]==bel[i+n])
{
ans=true;
break;
}
if(ans) puts("BAD");
else puts("GOOD");
}
return 0;
}