[BZOJ 1823] 满汉全席 2-SAT

题目传送门:【BZOJ 1823】

题目大意: ……为了招收新进的厨师进入世界满汉全席协会,近日该协会将举办满汉全席大赛。协会将派遣许多会员当作评委,为的就是要在参赛的厨师之中,找到满汉料理界的明日之星。每位参赛的选手可以得到 n 种菜品材料,选手可以自由选择用满式或是汉式料理将该材料当成菜肴。共有 m 位评审员分别把关,每一位评审员都有两种喜欢的菜品种类。只要参赛者能在评审员所喜爱的两种菜品中,做出任意一个符合评审喜好的菜品即可通过该评委的审查。但大会后来发现,在这样的制度下如果材料选择跟派出的评审员没有特别安排好的话,所有的参赛者最多只能通过部分评委的审查而不是全部,所以可能会发生没有人通过考核的情形。所以大会希望你能写一个程序来判断,所选出的 m 位评审,会不会发生没有人通过考核的窘境,以便协会组织合适的评审团。

输入共 K 组数据,每组数据第一行为两个整数 n , m,代表有 n 种菜品,m 位评审员(K ≤ 50 , n ≤ 100 , m ≤ 1000)。接下来的 m 行,每行都代表对应的评审员所拥有的两个喜好菜品,每个喜好菜品由一个英文字母跟一个数字代表(例如:m1 , h2)。每组数据输出一行,如果不会发生没有人通过考核的窘境,输出 GOOD;否则输出BAD(大写字母)。


题目分析:
一道 2-SAT 的模板题。想必看明白了 2-SAT 算法的同学在认真读完这道题之后就应该能想到吧。

对于每一种菜品,我们把它拆成两个点 h 和 m,代表它是被做成汉式菜或者是满式菜。显然一种菜品只能有一种选择。然后对于输入中给出的关系,由于两种菜品(A , B)中至少需要选择一个,所以我们建立(A’ -> B),(B’ -> A)两条边表示两种菜品至少要有一种满足评审员的喜好风格。之后按照常规跑 2-SAT 即可。

下面附上代码:

  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<iostream>  
  4. #include<algorithm>  
  5. #include<stack>  
  6. using namespace std;  
  7. const int MXN = 105,MXM = 1005;  
  8.   
  9. struct Edge{  
  10.     int to,next;  
  11. };  
  12. Edge edge[MXN * MXN * 2];  
  13. int n,m,head[MXN * 2],now = 0;  
  14. int dfn[MXN * 2],low[MXN * 2],cnt = 0,_index = 0,belong[MXN * 2];  
  15. stack<int> s;  
  16. bool ins[MXN * 2];  
  17.   
  18. int istype_Man(int c){                      //判断这个菜是否为满式菜   
  19.     if (c == ‘m’ || c == ‘M’return 1;     //在之后建边的时候使用   
  20.     return 0;  
  21. }  
  22.   
  23. void adde(int u,int v){  
  24.     edge[++now].to = v;  
  25.     edge[now].next = head[u];  
  26.     head[u] = now;  
  27. }  
  28.   
  29. void tarjan(int u){  
  30.     dfn[u] = low[u] = ++_index;  
  31.     s.push(u);  
  32.     ins[u] = true;  
  33.     for (int i = head[u];i;i = edge[i].next){  
  34.         int v = edge[i].to;  
  35.         if (!dfn[v]){  
  36.             tarjan(v);  
  37.             if (low[v] < low[u])  
  38.                 low[u] = low[v];  
  39.         } else if (ins[v] && dfn[v] < low[u])  
  40.             low[u] = dfn[v];  
  41.     }  
  42.     if (dfn[u] == low[u]){  
  43.         ++cnt;  
  44.         int tmp;  
  45.         do {  
  46.             tmp = s.top();  
  47.             s.pop();  
  48.             belong[tmp] = cnt;  
  49.             ins[tmp] = false;  
  50.         }while (tmp != u);  
  51.     }  
  52. }  
  53.   
  54. void _init(){  
  55.     memset(belong,0,sizeof(belong));  
  56.     memset(dfn,0,sizeof(dfn));  
  57.     memset(low,0,sizeof(low));  
  58.     memset(edge,0,sizeof(edge));  
  59.     memset(head,0,sizeof(head));  
  60.     now = 0,cnt = 0,_index = 0;  
  61. }  
  62.   
  63. int main(){  
  64.     int T,a,b;                      //a,b:菜品材料编号   
  65.     char type1,type2;               //type1,type2:对应的族系(汉 / 满)   
  66.     cin>>T;  
  67.     while (T–){  
  68.         cin>>n>>m;  
  69.         for (int i = 1;i <= m;i++){  
  70.             cin>>type1>>a>>type2>>b;  
  71.             //scanf(“%c%d %c%d”,&type1,&a,&type2,&b);  
  72.             if (a>b) swap(type1,type2),swap(a,b);  
  73.             a += istype_Man(type1) * n;  
  74.             b += istype_Man(type2) * n;  
  75.               
  76.             if (a <= n) adde(a + n,b);  
  77.             else adde(a - n,b);  
  78.             if (b <= n) adde(b + n,a);  
  79.             else adde(b - n,a);  
  80.         }  
  81.         for (int i = 1;i <= 2 * n;i++){  
  82.             if (!dfn[i]) tarjan(i);  
  83.         }  
  84.           
  85.         bool reach = false;  
  86.         for (int i = 1;i <= n;i++){  
  87.             if (belong[i] == belong[i + n]){  
  88.                 reach = true;  
  89.                 break;  
  90.             }  
  91.         }  
  92.         if (!reach) printf(“GOOD\n”);  
  93.         else printf(“BAD\n”);  
  94.           
  95.         _init();  
  96.           
  97.     }  
  98.     return 0;  
  99. }  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值