【bzoj1823】[JSOI2010]满汉全席 2-SAT
题目描述
满汉全席是中国最丰盛的宴客菜肴,有许多种不同的材料透过满族或是汉族的料理方式,呈现在數量繁多的菜色之中。由于菜色众多而繁杂,只有极少數博学多闻技艺高超的厨师能够做出满汉全席,而能够烹饪出经过专家认证的满汉全席,也是中国厨师最大的荣誉之一。 世界满汉全席协会是由能够料理满汉全席的专家厨师们所组成,而他们之间还细分为许多不同等级的厨师。为了招收新进的厨师进入世界满汉全席协会,将于近日举办满汉全席大赛,协会派遣许多会员当作评审员,为的就是要在參赛的厨师之中,找到满汉料理界的明日之星。 大会的规则如下:每位參赛的选手可以得到n 种材料,选手可以自由选择用满式或是汉式料理将材料当成菜肴。大会的评审制度是:共有m 位评审员分别把关。每一位评审员对于满汉全席有各自独特的見解,但基本见解是,要有兩样菜色作为满汉全席的标志。如某评审认为,如果没有汉式东坡肉跟满式的涮羊肉锅,就不能算是满汉全席。但避免过于有主見的审核,大会规定一个评审员除非是在认为必备的两样菜色都没有做出來的狀况下,才能淘汰一位选手,否则不能淘汰一位參赛者。换句话說,只要參赛者能在这兩种材料的做法中,其中一个符合评审的喜好即可通过该评审的审查。如材料有猪肉,羊肉和牛肉时,有四位评审员的喜好如下表: 评审一 评审二 评审三 评审四 满式牛肉 满式猪肉 汉式牛肉 汉式牛肉 汉式猪肉 满式羊肉 汉式猪肉 满式羊肉 如參赛者甲做出满式猪肉,满式羊肉和满式牛肉料理,他将无法满足评审三的要求,无法通过评审。而參赛者乙做出汉式猪肉,满式羊肉和满式牛肉料理,就可以满足所有评审的要求。 但大会后來发现,在这样的制度下如果材料选择跟派出的评审员没有特别安排好的话,所有的參赛者最多只能通过部分评审员的审查而不是全部,所以可能会发生没有人通过考核的情形。如有四个评审员喜好如下表时,则不論參赛者采取什么样的做法,都不可能通过所有评审的考核: 评审一 评审二 评审三 评审四 满式羊肉 满式猪肉 汉式羊肉 汉式羊肉 汉式猪肉 满式羊肉 汉式猪肉 满式猪肉 所以大会希望有人能写一个程序來判断,所选出的m 位评审,会不会发生 没有人能通过考核的窘境,以便协会组织合适的评审团。
输入
第一行包含一个数字 K,代表测试文件包含了K 组资料。每一组测试资料的第一行包含兩个数字n 跟m(n≤100,m≤1000),代表有n 种材料,m 位评审员。为方便起見,材料舍弃中文名称而给予编号,编号分别从1 到n。接下來的m 行,每行都代表对应的评审员所拥有的兩个喜好,每个喜好由一个英文字母跟一个数字代表,如m1 代表这个评审喜欢第1 个材料透过满式料理做出來的菜,而h2 代表这个评审员喜欢第2 个材料透过汉式料理做出來的菜。每个测试文件不会有超过50 组测试资料
输出
每笔测试资料输出一行,如果不会发生没有人能通过考核的窘境,输出GOOD;否则输出BAD(大写字母)。
样例输入
2
3 4
m3 h1
m1 m2
h1 h3
h3 m2
2 4
h1 m2
m2 m1
h1 h2
m1 h2
样例输出
GOOD
BAD
题目大意
有n种材料和m个评委,每种材料只能做h类型或m类型之一,每个评委有两个某材料做某类型的要求,问能否满足所有评委的至少一个要求。
题解
2-SAT裸题
然而作为初学2-SAT的蒟蒻,并没有弄清2-SAT的本质——必选关系,于是yy了如下辣鸡建图:
每个评委看作两个点,如果两个评委的某道菜有冲突,则从这道菜向有冲突的另一道菜连边。
而正常的建图是这样的:
每种材料看作两个点,对于一名评委的要求,如果不满足第一个要求就必须满足第二个要求,所以第一道菜的相反类型向第二道菜连边,反之亦然。
当然对于这道水题来说这些都不重要,但是最重要的是读入。
一开始写了字符串读入,结果发现数字并不一定是一位数,所以比较起来很麻烦。最终选择了读入优化,并且将m当作负号来处理。
最后跑Tarjan,如果一对点在同一个强连通分量中则输出BAD,否则输出GOOD。
#include <cstdio> #include <cstring> #include <algorithm> #define N 2010 #define M 4000010 using namespace std; int head[N] , to[M] , next[M] , cnt , deep[N] , low[N] , tot , vis[N] , ins[N] , sta[N] , top , bl[N] , num , x1[N] , x2[N]; inline int read() { int ret = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9') {if(ch == 'm')f = -1; ch = getchar();} while(ch >= '0' && ch <= '9') ret = (ret << 3) + (ret << 1) + ch - '0' , ch = getchar(); return ret * f; } void add(int x , int y) { to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt; } void tarjan(int x) { int i; deep[x] = low[x] = ++tot , ins[x] = vis[x] = 1 , sta[++top] = x; for(i = head[x] ; i ; i = next[i]) { if(!vis[to[i]]) tarjan(to[i]) , low[x] = min(low[x] , low[to[i]]); else if(ins[to[i]]) low[x] = min(low[x] , deep[to[i]]); } if(deep[x] == low[x]) { int t; num ++ ; do { t = sta[top -- ] , ins[t] = 0 , bl[t] = num; }while(t != x); } } int main() { int T; T = read(); while(T -- ) { memset(head , 0 , sizeof(head)) , memset(vis , 0 , sizeof(vis)) , cnt = tot = 0; int n , m , i , j , flag = 1; read() , m = read(); for(i = 1 ; i <= m ; i ++ ) { x1[i] = read() , x2[i] = read(); for(j = 1 ; j < i ; j ++ ) { if(x1[j] + x1[i] == 0) add(j , i + m) , add(i , j + m); if(x1[j] + x2[i] == 0) add(j , i) , add(i + m , j + m); if(x2[j] + x1[i] == 0) add(j + m , i + m) , add(i , j); if(x2[j] + x2[i] == 0) add(j + m , i) , add(i + m , j); } } for(i = 1 ; i <= 2 * m ; i ++ ) if(!vis[i]) tarjan(i); for(i = 1 ; i <= m ; i ++ ) { if(bl[i] == bl[i + m]) { printf("BAD\n") , flag = 0; break; } } if(flag) printf("GOOD\n"); } return 0; }