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;
}