【Luogu 4171】[JSOI2010] 满汉全席(2-SAT)

目录

  • 题目
      • 题目描述
      • 输入格式
      • 输出格式
      • 输入输出样例
  • 思路
      • 2_SAT
          • 定义
          • 求解
  • 代码

题目

题目描述

满汉全席是中国最丰盛的宴客菜肴,有许多种不同的材料透过满族或是汉族的料理方式,呈现在數量繁多的菜色之中。由于菜色众多而繁杂,只有极少數博学多闻技艺高超的厨师能够做出满汉全席,而能够烹饪出经过专家认证的满汉全席,也是中国厨师最大的荣誉之一。世界满汉全席协会是由能够料理满汉全席的专家厨师们所组成,而他们之间还细分为许多不同等级的厨师。

为了招收新进的厨师进入世界满汉全席协会,将于近日举办满汉全席大赛,协会派遣许多会员当作评审员,为的就是要在參赛的厨师之中,找到满汉料理界的明日之星。

大会的规则如下:每位參赛的选手可以得到 n n n 种材料,选手可以自由选择用满式或是汉式料理将材料当成菜肴。

大会的评审制度是:共有 m m m 位评审员分别把关。每一位评审员对于满汉全席有各自独特的見解,但基本见解是,要有兩样菜色作为满汉全席的标志。如某评审认为,如果没有汉式东坡肉跟满式的涮羊肉锅,就不能算是满汉全席。但避免过于有主見的审核,大会规定一个评审员除非是在认为必备的两样菜色都没有做出來的狀况下,才能淘汰一位选手,否则不能淘汰一位參赛者。

换句话說,只要參赛者能在这兩种材料的做法中,其中一个符合评审的喜好即可通过该评审的审查。如材料有猪肉,羊肉和牛肉时,有四位评审员的喜好如下表:

评审一   评审二   评审三   评审四 
满式牛肉 满式猪肉 汉式牛肉 汉式牛肉 
汉式猪肉 满式羊肉 汉式猪肉 满式羊肉 

如參赛者甲做出满式猪肉,满式羊肉和满式牛肉料理,他将无法满足评审三的要求,无法通过评审。而參赛者乙做出汉式猪肉,满式羊肉和满式牛肉料理,就可以满足所有评审的要求。

但大会后來发现,在这样的制度下如果材料选择跟派出的评审员没有特别安排好的话,所有的參赛者最多只能通过部分评审员的审查而不是全部,所以可能会发生没有人通过考核的情形。

如有四个评审员喜好如下表时,则不论参赛者采取什么样的做法,都不可能通过所有评审的考核:

评审一   评审二   评审三   评审四 
满式羊肉 满式猪肉 汉式羊肉 汉式羊肉 
汉式猪肉 满式羊肉 汉式猪肉 满式猪肉 

所以大会希望有人能写一个程序來判断,所选出的 m m m 位评审,会不会发生没有人能通过考核的窘境,以便协会组织合适的评审团。

输入格式

第一行包含一个数字 K K K,代表测试文件包含了 K K K 组资料。

每一组测试资料的第一行包含兩个数字 n n n m    ( n ≤ 100 , m ≤ 1000 ) m\;(n≤100,m≤1000) m(n100m1000),代表有 n n n 种材料, m m m 位评审员。

为方便起見,材料舍弃中文名称而给予编号,编号分别从 1 1 1 n n n

接下來的 m m m 行,每行都代表对应的评审员所拥有的兩个喜好,每个喜好由一个英文字母跟一个数字代表,如 m 1 m1 m1 代表这个评审喜欢第 1 1 1 个材料透过满式料理做出來的菜,而 h 2 h2 h2 代表这个评审员喜欢第 2 2 2 个材料透过汉式料理做出來的菜。

每个测试文件不会有超过 50 50 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 − s a t 2-sat 2sat 模板。
(又是刚学会。。。)


2_SAT

定义

2 − s a t 2-sat 2sat 就是给你 n n n 0 / 1 0/1 0/1 变量 a 1 ,   a 2   ⋯   a n a_1,\:a_2\:\cdots\:a_n a1,a2an。同时给出若干个等式,每个等式形如   a i    o p     a j = 0 / 1 \:a_i \;op\; \: a_j=0/1 aiopaj=0/1,其中 o p op op 表示 a n d ,   o r ,   x o r and,\:or,\:xor and,or,xor 中的一种。
我们要选出可行的一组解 a a a
【Luogu 4171】[JSOI2010] 满汉全席(2-SAT)_第1张图片

那么我给 像我一样的蒟蒻们 举个栗子?:
有三个人: t 14 t14 t14 F a t m o u s e Fatmouse Fatmouse f u z h i a n fuzhian fuzhian
现在他们要共打一份代码,可是三人的代码风格却不同。
那这份代码风格应该是怎样的呢?

t 14 t14 t14 说,代码风格要满足以下几条中的一条:
1、用快读                                       a \;\;\;\;\;\;\;\;\;\;\;\;\:\;\;\;\;\;\;a a
2、大括号不换行                  ¬ b \;\;\;\;\;\;\;\;\neg b ¬b
3、符号两边加空格                c \;\;\;\;\;\;\;c c

可爱的 F a t m o u s e Fatmouse Fatmouse 说:代码风格要满足以下几条中的一条:
1、不用快读                               ¬ a \;\;\;\;\;\;\:\;\;\;\;\;\;\;\;\neg a ¬a
2、大括号换行                          b \;\;\;\;\;\;\;\;\;\;\;\; b b
3、符号两边不加空格        ¬ c \;\;\;\neg c ¬c

F u z h i a n Fuzhian Fuzhian 说:代码风格要满足以下几条中的一条:
1、不用快读                               ¬ a \;\;\;\;\;\;\:\;\;\;\;\;\;\;\;\neg a ¬a
2、大括号换行                          b \;\;\;\;\;\;\;\;\;\;\;\; b b
3、符号两边加空格                c \;\;\;\;\;\;\;c c

那么该怎么找到一种解决方案呢?
(这不和上面的是同一个东东吗?!)
没错,把上面的条件转换成式子就是这个样子的:
( a    o r    ¬ b   o r    c )      a n d      ( ¬ a    o r    b   o r    ¬ c )      a n d      ( ¬ a    o r    b   o r    c ) (a\;or\;\neg b\:or\;c)\;\;and\;\;(\neg a\;or\;b\:or\;\neg c)\;\;and\;\;(\neg a\;or\; b\:or\;c) (aor¬borc)and(¬aorbor¬c)and(¬aorborc)
然后给 a a a b b b c c c 分别取值,找到一组可行解(暴力就完事了)。
【Luogu 4171】[JSOI2010] 满汉全席(2-SAT)_第2张图片


求解

那么怎么变成求解 2 − s a t 2-sat 2sat 问题呢?

首先我们要学会建一张 可爱的 图:
选A就必须选B,相同的,不选A就必须不选B(很显然)。
那么就有以下几种情况:

  • A A A, B B B 不能同时选:选了 A A A 就要选 ¬ B \neg B ¬B ,选 B B B 就要选 ¬ A \neg A ¬A 。即 A → ¬ B ,    B → ¬ A A→\neg B,\;B→\neg A A¬B,B¬A
  • A A A, B B B 必须同时选:选了 A A A 就要选 B B B,选 B B B 就要选 A A A 。即 A → B ,    B → A A→B,\;B→A AB,BA
  • A A A, B B B 任选一个:选了 A A A 就要选 ¬ B \neg B ¬B,选 B B B 就要选 ¬ A \neg A ¬A,选 ¬ A \neg A ¬A 就要选 B B B,选 ¬ B \neg B ¬B就要选 A A A。即 A → ¬ B ,    B → ¬ A ,    ¬ A → B ,    ¬ B → A A→\neg B,\;B→\neg A,\;\neg A→B,\;\neg B→A A¬B,B¬A,¬AB,¬BA
  • A A A 必须选:即 ¬ A → A \neg A → A ¬AA

这样,可爱的 图就建好了。
然后捏?

—— D F S DFS DFS                \;\;\;\;\;\;\;
的确, D F S DFS DFS 是优秀的解法,并不慢。
这种做法还可以保证解的字典序,是一种不错的方法。

—— t a r j a n    +    tarjan \;+\; tarjan+拓扑                \;\;\;\;\;\;\;
T a r j a n Tarjan Tarjan 缩点,如果 A A A ¬ A \neg A ¬A 在同一强连通分量里那么显然无解。
然后整一个拓扑, A A A ¬ A \neg A ¬A 中拓扑序较大的一个就可以得到一组可行解。
其实并没有 D F S DFS DFS 好用,一般只是用来判断连通性。


那么这题怎么运用 2 − s a t 2-sat 2sat 呢?
貌似 很简单 的模板(只需 t a r j a n tarjan tarjan 判断即可)?

  • 1.    m i , m j 1.\;m_i,m_j 1.mi,mj:连边 i + n i+n i+n j j j,连边 j + n j+n j+n i i i
  • 2.    m i , h j 2.\;m_i,h_j 2.mi,hj:连边 i + n i+n i+n j + n j+n j+n,连边 j j j i i i
  • 3.    h i , h j 3.\;h_i,h_j 3.hi,hj:连边 i i i j + n j+n j+n, 连边 j j j i + n i+n i+n
  • 4.    h i , m j 4.\;h_i,m_j 4.hi,mj:连边 i i i j j j,连边 j + n j+n j+n i + n i+n i+n
  • (构图似乎很好理解?)
  • 然后我们愉快地跑跑 t a r j a n tarjan tarjan    ( 天 天 错 t a r j a n 的 我 ) \;\sout{(天天错 tarjan 的我)} tarjan
  • 跑完之后,看是否有同一种材料的选汉式做法选满式做法 在同一个强连通分量中,是否有同一种材料的同一道做法的不选 在同一强连通分量中。
  • 就完事了。 (的确很简 (jian)(nan)?)

代码

#include 

using namespace std;
struct node {
	int y, nxt;
} e[10010];
int dfn[1010], low[1010], cnt, n, m, tot, head[1010], c[1010], lev;
bool flg, b[1010];
stack <int> s;

void addedge(int x, int y) {
	e[++tot].y = y;
	e[tot].nxt = head[x];
	head[x] = tot;
}

void tarjan(int x) {
    dfn[x] = low[x] = ++lev;
    s.push(x); b[x] = 1;
    for (int i = head[x]; i; i = e[i].nxt) {
        int y = e[i].y;
        if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
        else if (b[y]) low[x] = min(low[x], dfn[y]);
    }
    if (low[x] == dfn[x]) {
        int t; cnt++;
        do {
            t = s.top();
            s.pop(); b[t] = 0;
            c[t] = cnt;
        } while (t != x);
    }
}

int main() {
	int T; scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &n, &m);
		flg = tot = cnt = lev = 0;
	    memset(head, 0, sizeof head); 
	    memset(c, 0, sizeof c);
	    memset(low, 0, sizeof low);
	    memset(dfn, 0, sizeof dfn);
	    memset(b, 0, sizeof b);
	    for (int i = 1; i <= m; i++) {
	    	char t1[10], t2[10];
	    	scanf("%s%s", t1, t2);
	    	int x = 0, y = 0;
	    	for (int k = 1; t1[k] >= '0' && t1[k] <= '9'; k++) x = x*10+t1[k]-'0';
			for (int k = 1; t2[k] >= '0' && t2[k] <= '9'; k++) y = y*10+t2[k]-'0';
			if (t1[0] == 'm')
                if (t2[0] == 'h') addedge(x+n, y+n), addedge(y, x);
                else if (t2[0] == 'm') addedge(x+n, y), addedge(y+n, x);
    		if (t1[0] == 'h')
                if (t2[0] == 'h') addedge(x, y+n), addedge(y, x+n);
                else if (t2[0] == 'm') addedge(x, y), addedge(y+n, x+n);
		}
		for (int i = 1; i <= n*2; i++)
			if (dfn[i] == 0) tarjan(i);
        for (int i = 1; i <= n; i++)
        	if (c[i] == c[i+n]) {flg = 1; break;}
        flg == 1 ? puts("BAD") : puts("GOOD");
	}
	return 0;
}

你可能感兴趣的:(图论,#,2-SAT)