[JSOI2010] 满汉全席
2-SAT裸题。
简单讲一下2-SAT:
首先把题目给出的种种限制转换为一个图。
以这道题来说,每种菜看作是两个点,一个点代表做成汉式,另一个代表做成满式。
然后考虑约束条件:
比如:评委要求A做成汉式,B做成满式。
那么如果A做成满式,B就一定是满式;同理,如果B做成汉式,那A一定是汉式。
所以这么连边:A满-->B满、B汉->A汉。
不同的题连边方法也略有不同。
然后对这个图求一个强连通分量。
显然,如果选择了某个点,那么那个点所在的强连通分量里的所有点都要选择。
对于1<=X<=n:
如果存在某个X,X汉和X满在一个强连通分量里,就无解。
否则有解。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int k; 7 int n,m; 8 int hd[205],to[2005],nx[2005],ec; 9 10 int dish(int id,int kd) 11 { 12 return id*2-kd; 13 } 14 15 void edge(int af,int at) 16 { 17 to[++ec]=at; 18 nx[ec]=hd[af]; 19 hd[af]=ec; 20 } 21 22 int dfn[205],low[205],dc; 23 int in[205],st[205],tp; 24 int gr[205],gsz[205],gc; 25 26 void tarjan(int p) 27 { 28 dfn[p]=low[p]=++dc; 29 st[++tp]=p; 30 in[p]=1; 31 for(int i=hd[p];i;i=nx[i]) 32 { 33 if(!dfn[to[i]])tarjan(to[i]),low[p]=min(low[p],low[to[i]]); 34 else if(in[to[i]])low[p]=min(low[p],dfn[to[i]]); 35 } 36 if(low[p]==dfn[p]) 37 { 38 gc++; 39 int np=0; 40 while(np!=p) 41 { 42 np=st[tp--]; 43 gr[np]=gc; 44 gsz[gc]++; 45 in[np]=0; 46 } 47 } 48 } 49 50 void clean() 51 { 52 memset(hd,0,sizeof(hd)); 53 memset(dfn,0,sizeof(dfn)); 54 memset(low,0,sizeof(low)); 55 memset(gsz,0,sizeof(gsz)); 56 dc=ec=gc=0; 57 } 58 59 int main() 60 { 61 scanf("%d",&k); 62 while(k--) 63 { 64 clean(); 65 scanf("%d%d",&n,&m); 66 for(int i=1;i<=m;i++) 67 { 68 int id[2]={0,0},kd[2],p; 69 char lim[10]; 70 for(int j=0;j<=1;j++) 71 { 72 scanf("%s",lim+1); 73 p=1; 74 kd[j]=(lim[p++]=='m'); 75 while(lim[p]>='0'&&lim[p]<='9') 76 id[j]=id[j]*10+lim[p++]-'0'; 77 } 78 edge(dish(id[0],kd[0]^1),dish(id[1],kd[1])); 79 edge(dish(id[1],kd[1]^1),dish(id[0],kd[0])); 80 } 81 for(int i=1;i<=2*n;i++) 82 if(!dfn[i])tarjan(i); 83 int fl=1; 84 for(int i=1;i<=n;i++) 85 { 86 if(gr[dish(i,0)]==gr[dish(i,1)]) 87 { 88 fl=0; 89 break; 90 } 91 } 92 if(fl)printf("GOOD\n"); 93 else printf("BAD\n"); 94 } 95 return 0; 96 }