Bzoj P1823 [JSOI2010]满汉全席___2-SAT

9 篇文章 0 订阅
7 篇文章 0 订阅

题目大意:

数据 T T T组,
n n n道材料, m m m个评委,
每个评委会有2个要求, ( a , x ) , ( b , y ) (a,x),(b,y) (a,x)(b,y)
x , y x,y x,y为材料编号, a , b a,b a,b为做法,做法仅有满式或者汉式
对于一个评委被满足,至少有一个要求被满足
每道材料有且只有一道,即每道材料只能被一种做法使用,
问是否有一种方案能使得所有的评委都满足
对于每组数据,有输出 G O O D GOOD GOOD,没有输出 B A D BAD BAD

n ≤ 100 , m ≤ 1000 n≤100,m≤1000 n100m1000
T ≤ 50 T≤50 T50

分析:

每种材料分成汉式跟满式做法,
然后可以可以转化成 2 − S A T 2-SAT 2SAT的模型,
[ 1 , n ] 满 式 做 法 处 理 材 料 1   n [1,n] 满式做法处理材料1~n [1,n]1 n
[ n + 1 , 2 ∗ n ] 汉 式 做 法 处 理 材 料 1   n [n+1,2*n] 汉式做法处理材料1~n [n+1,2n]1 n
[ 2 ∗ n + 1 , 3 ∗ n ] 不 用 满 式 做 法 处 理 材 料 1   n [2*n+1,3*n] 不用满式做法处理材料1~n [2n+1,3n]1 n
[ 3 ∗ n + 1 , 4 ∗ n ] 不 用 汉 式 做 法 处 理 材 料 1   n [3*n+1,4*n] 不用汉式做法处理材料1~n [3n+1,4n]1 n
考虑建模,
(1)不选汉式做法与选满式做法间连双向边
(2)不选满式做法与选汉式做法间连双向边
(3)对于每个要求,连2条单向边:
①不做第一道菜->做第二道菜
②不做第二道菜->做第一道菜
然后就是经典的 2 − S A T 2-SAT 2SAT判定问题,
利用tarjan缩点以后,
如果
①同一道材料的2种做法在同一个SCC中
②同一种做法的做与不做在同一个SCC中
则是无解的
否则有解

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdlib>
#include <algorithm>

#define mt(x) memset(x, 0, sizeof(x)) 
#define N 105

using namespace std;

struct Node { int To, nxt; }e[2505];
int Belong[N*4], Instack[N*4], dfn[N*4], low[N*4], ls[N*4], T, n, m, cnt, cdp, total;  
char AA[5], BB[5];

stack <int> st;

void Pre_Work()
{
    mt(Instack); mt(Belong); mt(dfn); mt(low); mt(ls);
    cnt = 0; cdp = 0; total = 0;
}

void Read_Work(int &x)
{
    char c = getchar();
    while (c != 'h' && c != 'm') c = getchar();
	scanf("%d", &x);
	if (c == 'h') x = x + n; 
}

void Addedge(int u, int v)
{
    e[++cnt].To = v, e[cnt].nxt = ls[u], ls[u] = cnt;	
}

void Tarjan(int u)
{
    dfn[u] = low[u] = ++cdp;
	Instack[u] = 1;
	st.push(u);
	for (int i = ls[u]; i; i = e[i].nxt)
	{
	    int v = e[i].To;
		if (!dfn[v]) Tarjan(v), low[u] = min(low[u], low[v]);
		   else if (Instack[v]) low[u] = min(low[u], dfn[v]);
	}
	if (dfn[u] == low[u]) 
	{
	    ++total; int yzh;
	    while ("displaylzy")
	    {
	    	yzh = st.top(); st.pop();
	    	Belong[yzh] = total, Instack[yzh] = 0;
	    	if (yzh == u) break;
		}
			
	}	
}

// 1~n 满式做法处理材料1~n 
// n+1~2*n 汉式做法处理材料1~n 
// 2*n+1~3*n 不用满式做法处理材料1~n
// 3*n+1~4*n 不用汉式做法处理材料1~n

int main()
{
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d %d", &n, &m);
		Pre_Work();
		for (int i = 1; i <= m; i++)
		{
			int AA, BB;
		    Read_Work(AA);
		    Read_Work(BB);
		    Addedge(AA + 2 * n, BB);
		    Addedge(BB + 2 * n, AA);
		}
		for (int i = 1; i <= n; i++) 
		{
		    Addedge(2 * n + i, n + i); Addedge(n + i, 2 * n + i);
		    Addedge(3 * n + i, i); Addedge(i, 3 * n + i);
		}
		for (int i = 1; i <= 4 * n; i++) if (!dfn[i]) Tarjan(i);
		bool Check = 0;
		for (int i = 1; i <= n; i++) 
		    if (Belong[i] == Belong[n + i] || Belong[i] == Belong[2 * n + i] || Belong[n + i] == Belong[3 * n + i]) Check = 1;
		if (Check) printf("BAD\n"); else printf("GOOD\n");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值