#HDU 3062 Party (2-SAT)

Party

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 9724    Accepted Submission(s): 3048


 

Problem Description

有n对夫妻被邀请参加一个聚会,因为场地的问题,每对夫妻中只有1人可以列席。在2n 个人中,某些人之间有着很大的矛盾(当然夫妻之间是没有矛盾的),有矛盾的2个人是不会同时出现在聚会上的。有没有可能会有n 个人同时列席?

 

 

Input

n: 表示有n对夫妻被邀请 (n<= 1000)
m: 表示有m 对矛盾关系 ( m < (n - 1) * (n -1))

在接下来的m行中,每行会有4个数字,分别是 A1,A2,C1,C2
A1,A2分别表示是夫妻的编号
C1,C2 表示是妻子还是丈夫 ,0表示妻子 ,1是丈夫
夫妻编号从 0 到 n -1

 

 

Output

如果存在一种情况 则输出YES
否则输出 NO

 

 

Sample Input

 

2 1 0 1 1 1

 

 

Sample Output

 

YES

题目大意 : 有N对夫妻, M对敌对关系, 有敌对关系的人无法出席同一场宴会, 夫妻当中必须有一个人出席, 问你是否可以满足条件,如果可以, 输出 YES, 否则 输出 NO

思路 : 经典的2-SAT问题, 2-SAT是集合中数量为1选其中一个,并附加一些限制条件, 当一个点存在时,一定存在另一个点与之相连(两个点必须选一个, 一个点不能选, 所以必须连一个点),最后判断每个点和他对应的点是否在一个环内, 如果在说明不满足条件

Accepted code

#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;

#define sc scanf
#define ls rt << 1
#define rs ls | 1
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define MEM(x, b) memset(x, b, sizeof(x))
#define lowbit(x) ((x) & -(x))
#define P2(x) ((x) * (x))

typedef long long ll;
const int MOD = 1e9 + 7;
const int MAXN = 2e3 + 100;
const int INF = 0x3f3f3f3f;
inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % MOD; b >>= 1; t = (t*t) % MOD; }return r; }

vector <int> e[MAXN << 1];
int dfn[MAXN], low[MAXN], n, m, tot;
int suo[MAXN], scnt;
bool vis[MAXN];
stack <int> st;
void init() {
	for (int i = 0; i < 2 * n; i++) dfn[i] = low[i] = suo[i] = 0, vis[i] = false, e[i].clear();
	tot = scnt = 0;
}
void tarjan(int x, int fa) {
	dfn[x] = low[x] = ++tot;
	st.push(x); vis[x] = true;
	for (int i = 0; i < SZ(e[x]); i++) {
		int vi = e[x][i];
		if (!dfn[vi]) {
			tarjan(vi, x);
			Min(low[x], low[vi]);
		}
		else if (vis[vi]) Min(low[x], dfn[vi]);
	}
	if (dfn[x] == low[x]) {
		scnt++; int k;
		do {
			k = st.top(); st.pop();
			vis[k] = false;
			suo[k] = scnt;
		} while (k != x);
	}
}

int main()
{
	while (~sc("%d %d", &n, &m)) {
		init();
		for (int i = 0; i < m; i++) {
			int a1, a2, c1, c2, ui, vi;
			sc("%d %d %d %d", &a1, &a2, &c1, &c2);
			ui = a1, vi = a2;
			if (c1) ui += n; if (c2) vi += n;
			if (c2) e[ui].push_back(vi - n);
			else e[ui].push_back(vi + n);
			if (c1) e[vi].push_back(ui - n);
			else e[vi].push_back(ui + n);
		}
		for (int i = 0; i < 2 * n; i++) {  // 缩点判环
			if (!dfn[i]) tarjan(i, i);
		}
		bool flag = true;
		for (int i = 0; i < n; i++) {
			if (suo[i] == suo[i + n]) {  // 对应的点在一个集合
				flag = false;
				break;
			}
		}
		if (flag) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值