P4171 [JSOI2010]满汉全席
P4171 [JSOI2010]满汉全席
题目描述
满汉全席是中国最丰盛的宴客菜肴,有许多种不同的材料透过满族或是汉族的料理方式,呈现在數量繁多的菜色之中。由于菜色众多而繁杂,只有极少數博学多闻技艺高超的厨师能够做出满汉全席,而能够烹饪出经过专家认证的满汉全席,也是中国厨师最大的荣誉之一。世界满汉全席协会是由能够料理满汉全席的专家厨师们所组成,而他们之间还细分为许多不同等级的厨师。
为了招收新进的厨师进入世界满汉全席协会,将于近日举办满汉全席大赛,协会派遣许多会员当作评审员,为的就是要在參赛的厨师之中,找到满汉料理界的明日之星。
大会的规则如下:每位參赛的选手可以得到n 种材料,选手可以自由选择用满式或是汉式料理将材料当成菜肴。
大会的评审制度是:共有m 位评审员分别把关。每一位评审员对于满汉全席有各自独特的見解,但基本见解是,要有兩样菜色作为满汉全席的标志。如某评审认为,如果没有汉式东坡肉跟满式的涮羊肉锅,就不能算是满汉全席。但避免过于有主見的审核,大会规定一个评审员除非是在认为必备的两样菜色都没有做出來的狀况下,才能淘汰一位选手,否则不能淘汰一位參赛者。
换句话說,只要參赛者能在这兩种材料的做法中,其中一个符合评审的喜好即可通过该评审的审查。如材料有猪肉,羊肉和牛肉时,有四位评审员的喜好如下表:
评审一 评审二 评审三 评审四
满式牛肉 满式猪肉 汉式牛肉 汉式牛肉
汉式猪肉 满式羊肉 汉式猪肉 满式羊肉
如參赛者甲做出满式猪肉,满式羊肉和满式牛肉料理,他将无法满足评审三的要求,无法通过评审。而參赛者乙做出汉式猪肉,满式羊肉和满式牛肉料理,就可以满足所有评审的要求。
但大会后來发现,在这样的制度下如果材料选择跟派出的评审员没有特别安排好的话,所有的參赛者最多只能通过部分评审员的审查而不是全部,所以可能会发生没有人通过考核的情形。
如有四个评审员喜好如下表时,则不论参赛者采取什么样的做法,都不可能通过所有评审的考核:
评审一 评审二 评审三 评审四
满式羊肉 满式猪肉 汉式羊肉 汉式羊肉
汉式猪肉 满式羊肉 汉式猪肉 满式猪肉
所以大会希望有人能写一个程序來判断,所选出的m 位评审,会不会发生 没有人能通过考核的窘境,以便协会组织合适的评审团。
输入输出格式
输入格式:
第一行包含一个数字 K,代表测试文件包含了K 组资料。
每一组测试资料的第一行包含兩个数字n 跟m(n≤100,m≤1000),代表有n 种材料,m 位评审员。
为方便起見,材料舍弃中文名称而给予编号,编号分别从1 到n。
接下來的m 行,每行都代表对应的评审员所拥有的兩个喜好,每个喜好由一个英文字母跟一个数字代表,如m1 代表这个评审喜欢第1 个材料透过满式料理做出來的菜,而h2 代表这个评审员喜欢第2 个材料透过汉式料理做出來的菜。
每个测试文件不会有超过50 组测试资料
输出格式:
每笔测试资料输出一行,如果不会发生没有人能通过考核的窘境,输出GOOD;否则输出BAD(大写字母)。
我真他么日了狗了, 就一个 \(getchar\) , 10分, 本地测评和线上测评输出答案不一样? 哇吃了没文化的亏。
错误日志:用了getchar读入字符, 下次老老实实用 cin 读一整句吧
我这是常规写法。。 能过, 慢, 空间还大, 可是好想
一种材料拆成四个点(分别用 \(n * 4,n * 4 + 1, n * 4 + 2, n * 4 + 3\) 代表汉式不做,汉式做,满式不做,满式做),对于同一种材料满足汉式满式二选一,(即 汉式 \(xor\) 满式 \(= 1\),连边如下:)
for(int i = 1;i <= num;i++){
int x = i * 2, y = i * 2 + 1;
add(x << 1, y << 1 | 1, 1);
add(y << 1, x << 1 | 1, 1);
add(x << 1 | 1, y << 1, 1);
add(y << 1 | 1, x << 1, 1);
}
对于每个评委的要求满足两个至少选其一(即 要求\(1\) \(or\) 要求\(2 = 1\),连边如下)
int get_index(){
char s[19];
cin>>s;
int i = 1, len = strlen(s), out = 0;
while(i < len)out = out * 10 + s[i++] - '0';
out <<= 1;
if(s[0] == 'm')out++;
return out;
}
for(int i = 1;i <= nr;i++){
int x = get_index(), y = get_index();
add(x << 1, y << 1 | 1, 1);
add(y << 1, x << 1 | 1, 1);
}
检查时若是一种材料同时做了满式和汉式(即col[做汗] = col[做满] ),或是一样菜同时要求做和不做(即 col[做] = col[不做] )则不合法,代码如下:
bool check(){
for(int i = 1;i <= num;i++){
int x = i << 1, y = i << 1 | 1;
if(col[x << 1 | 1] == col[y << 1 | 1])return 0;
}
for(int i = 2;i <= (num << 1 | 1);i++){
if(col[i << 1] == col[i << 1 | 1])return 0;
}
return 1;
}
其余直接跑 \(2-SAT\) 即可
Code
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
typedef long long LL;
using namespace std;
int RD(){
int out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
const int maxn = 2000019,INF = 1e9 + 19;
int head[maxn],nume = 1;
struct Node{
int v,dis,nxt;
}E[maxn << 2];
void add(int u,int v,int dis){
E[++nume].nxt = head[u];
E[nume].v = v;
E[nume].dis = dis;
head[u] = nume;
}
int get_index(){
char s[19];
cin>>s;
int i = 1, len = strlen(s), out = 0;
while(i < len)out = out * 10 + s[i++] - '0';
out <<= 1;
if(s[0] == 'm')out++;
return out;
}
int num, nr, T;
int DFN[maxn], LOW[maxn], INDEX;
int S[maxn], top;
bool ins[maxn];
int col[maxn], numc;
void Tarjan(int u){
DFN[u] = LOW[u] = ++INDEX;
S[++top] = u;ins[u] = 1;
for(int i = head[u];i;i = E[i].nxt){
int v = E[i].v;
if(!DFN[v])Tarjan(v), LOW[u] = min(LOW[u], LOW[v]);
else if(ins[v])LOW[u] = min(LOW[u], DFN[v]);
}
if(DFN[u] == LOW[u]){
numc++;
while(S[top + 1] != u){
col[S[top]] = numc;
ins[S[top--]] = 0;
}
}
}
void init(){
memset(head, 0, sizeof(head));
nume = 1;
memset(DFN, 0, sizeof(DFN));
memset(LOW, 0, sizeof(LOW));
INDEX = 0;
memset(col, 0, sizeof(col));
numc = 0;
}
bool check(){
for(int i = 1;i <= num;i++){
int x = i << 1, y = i << 1 | 1;
if(col[x << 1 | 1] == col[y << 1 | 1])return 0;
}
for(int i = 2;i <= (num << 1 | 1);i++){
if(col[i << 1] == col[i << 1 | 1])return 0;
}
return 1;
}
int main(){
T = RD();
while(T--){
init();
num = RD();nr = RD();
for(int i = 1;i <= nr;i++){
int x = get_index(), y = get_index();
add(x << 1, y << 1 | 1, 1);
add(y << 1, x << 1 | 1, 1);
}
for(int i = 1;i <= num;i++){
int x = i * 2, y = i * 2 + 1;
add(x << 1, y << 1 | 1, 1);
add(y << 1, x << 1 | 1, 1);
add(x << 1 | 1, y << 1, 1);
add(y << 1 | 1, x << 1, 1);
}
for(int i = 4;i <= (num << 2) + 3;i++)if(!DFN[i])Tarjan(i);
if(check())puts("GOOD");
else puts("BAD");
}
return 0;
}