1823: [JSOI2010]满汉全席
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 855 Solved: 408
[ Submit][ Status]
Description
满汉全席是中国最丰盛的宴客菜肴,有许多种不同的材料透过满族或是汉族的料理方式,呈现在數量繁多的菜色之中。由于菜色众多而繁杂,只有极少數博学多闻技艺高超的厨师能够做出满汉全席,而能够烹饪出经过专家认证的满汉全席,也是中国厨师最大的荣誉之一。 世界满汉全席协会是由能够料理满汉全席的专家厨师们所组成,而他们之间还细分为许多不同等级的厨师。为了招收新进的厨师进入世界满汉全席协会,将于近日举办满汉全席大赛,协会派遣许多会员当作评审员,为的就是要在參赛的厨师之中,找到满汉料理界的明日之星。 大会的规则如下:每位參赛的选手可以得到n 种材料,选手可以自由选择用满式或是汉式料理将材料当成菜肴。大会的评审制度是:共有m 位评审员分别把关。每一位评审员对于满汉全席有各自独特的見解,但基本见解是,要有兩样菜色作为满汉全席的标志。如某评审认为,如果没有汉式东坡肉跟满式的涮羊肉锅,就不能算是满汉全席。但避免过于有主見的审核,大会规定一个评审员除非是在认为必备的两样菜色都没有做出來的狀况下,才能淘汰一位选手,否则不能淘汰一位參赛者。换句话說,只要參赛者能在这兩种材料的做法中,其中一个符合评审的喜好即可通过该评审的审查。如材料有猪肉,羊肉和牛肉时,有四位评审员的喜好如下表: 评审一 评审二 评审三 评审四 满式牛肉 满式猪肉 汉式牛肉 汉式牛肉 汉式猪肉 满式羊肉 汉式猪肉 满式羊肉 如參赛者甲做出满式猪肉,满式羊肉和满式牛肉料理,他将无法满足评审三的要求,无法通过评审。而參赛者乙做出汉式猪肉,满式羊肉和满式牛肉料理,就可以满足所有评审的要求。 但大会后來发现,在这样的制度下如果材料选择跟派出的评审员没有特别安排好的话,所有的參赛者最多只能通过部分评审员的审查而不是全部,所以可能会发生没有人通过考核的情形。如有四个评审员喜好如下表时,则不論參赛者采取什么样的做法,都不可能通过所有评审的考核: 评审一 评审二 评审三 评审四 满式羊肉 满式猪肉 汉式羊肉 汉式羊肉 汉式猪肉 满式羊肉 汉式猪肉 满式猪肉 所以大会希望有人能写一个程序來判断,所选出的m 位评审,会不会发生 没有人能通过考核的窘境,以便协会组织合适的评审团。
Input
第一行包含一个数字 K,代表测试文件包含了K 组资料。每一组测试资料的第一行包含兩个数字n 跟m(n≤100,m≤1000),代表有n 种材料,m 位评审员。为方便起見,材料舍弃中文名称而给予编号,编号分别从1 到n。接下來的m 行,每行都代表对应的评审员所拥有的兩个喜好,每个喜好由一个英文字母跟一个数字代表,如m1 代表这个评审喜欢第1 个材料透过满式料理做出來的菜,而h2 代表这个评审员喜欢第2 个材料透过汉式料理做出來的菜。每个测试文件不会有超过50 组测试资料,而每笔测试资料中材料的种类数跟评审员的个数均不超过15。
Output
每笔测试资料输出一行,如果不会发生没有人能通过考核的窘境,输出GOOD;否则输出BAD(大写字母)。
Sample Input
2
3 4
m3 h1
m1 m2
h1 h3
h3 m2
2 4
h1 m2
m2 m1
h1 h2
m1 h2
3 4
m3 h1
m1 m2
h1 h3
h3 m2
2 4
h1 m2
m2 m1
h1 h2
m1 h2
Sample Output
GOOD
BAD
BAD
2-SAT建模+可行性判断好题。
题目中说每个评委的要求至少要满足一个,而汉式和满式只能选择做一种,
那么这个就是模型:
(A,B)不能同时不取
此时的连边方法是:A'与B连,B'与A连
然后套上可行性判断的模板来做就行了。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <stack>
#define M 2005
using namespace std;
char s[10];
stack<int> S;
int tot,h[M],sccno[M],pre[M],lowlink[M],n,m,T,dfs_clock,scc_cnt;
struct edge
{
int ne,y;
}e[M*M];
void dfs(int u)
{
pre[u]=lowlink[u]=++dfs_clock;
S.push(u);
for (int i=h[u];i;i=e[i].ne)
{
int v=e[i].y;
if (!pre[v])
{
dfs(v);
lowlink[u]=min(lowlink[v],lowlink[u]);
}
else if (!sccno[v])
lowlink[u]=min(lowlink[u],pre[v]);
}
if (lowlink[u]==pre[u])
{
scc_cnt++;
for (;;)
{
int x=S.top();
S.pop();
sccno[x]=scc_cnt;
if (x==u) break;
}
}
}
void Findscc()
{
dfs_clock=scc_cnt=0;
memset(pre,0,sizeof(pre));
memset(sccno,0,sizeof(sccno));
for (int i=2;i<=2*n+1;i++)
if (!pre[i]) dfs(i);
}
void Add(int x,int y)
{
tot++;
e[tot].ne=h[x];
e[tot].y=y;
h[x]=tot;
}
int main()
{
scanf("%d",&T);
while (T--)
{
tot=0;
scanf("%d%d",&n,&m);
for (int i=2;i<=n*2+1;i++)
h[i]=0;
for (int i=1;i<=m;i++)
{
int x,y;
char c1,c2;
getchar();
scanf("%c%d %c%d",&c1,&x,&c2,&y);
x*=2,y*=2;
x+=(c1=='m');
y+=(c2=='m');
Add(x^1,y);Add(y^1,x);
}
Findscc();
bool f=true;
for (int i=2;i<=2*n;i+=2)
if (sccno[i]==sccno[i^1])
{
f=false;
break;
}
if (f) printf("GOOD\n");
else printf("BAD\n");
}
return 0;
}
感悟:
1.WA:把读入按照char[]来处理,默认是一位数字了。。
2.对2-SAT的理解:
Q:为什么要把图反向呢?
A:当选择了A点,那么A'被标记为不选择,此时要把不选择的标记向前面传递(A'不选择,A'之前的一定不选择,A'之后的不一定)。
如果此时图没有反向,那么要找到A'之前的点很麻烦(现在连得边都是向后走的);
而反向之后直接沿着边走,就相当于沿着反向边走了!
3.做了三道2—SAT题目,2—SAT题目建模方法为:
首先找到每个情况中(A,B)是哪一种模型:
那么A所对应的A'是谁呢?
有的题目中需要枚举每一种情况,与A不相容的都是A'(【POJ 3683】);
而在本题中,与A不相容的都已知了(h与m不相容),直接建图即可。