HDU 4115 Eliminate the Conflict (2-SAT判断)

思路:这道题,自己想的思路比别的博客稍微复杂一些,感觉像3-sat(虽然不知道3-sat是啥)
首先,u,v之间有限制,所以我们先按k=0和k=1分类
当k=0时,u,v两局必须要出一样的,然后再按Bob的u,v两局出的是否一样分类讨论
当k=1时,u,v两局必须要出一样的,然后再按Bob的u,v两局出的是否一样分类讨论
具体讨论见代码注释

#include
using namespace std;
const int N = 1e5 + 10;
const int M = 1e6 + 10;
int n, m;
int head[N], tot;
int b[N];
int sccno[N], scc_cnt, dfn[N], low[N], idx;
stackst;
struct Edge {
	int to;
	int nxt;
}e[M];
void add(int u, int v) {
	e[++tot].to = v;
	e[tot].nxt = head[u];
	head[u] = tot;
}
void dfs(int u) {
	st.push(u);
	dfn[u] = low[u] = ++idx;
	for (int v,i = head[u]; i != -1; i = e[i].nxt) {
		v = e[i].to;
		if (!dfn[v]) {
			dfs(v);
			low[u] = min(low[u], low[v]);
		}
		else if (!sccno[v])
			low[u] = min(low[u], dfn[v]);
	}
	if (dfn[u] == low[u]) {
		scc_cnt++;
		while (1) {
			int x = st.top();
			st.pop();
			sccno[x] = scc_cnt;
			if (x == u)break;
		}
	}
}
bool find_scc() {
	memset(sccno, 0, sizeof(sccno));
	memset(dfn, 0, sizeof(dfn));
	memset(low, 0, sizeof(low));
	scc_cnt = idx = 0;
	for (int i = 1; i <= 3 * n; i++)
		if (!dfn[i])dfs(i);
	for (int i = 1; i <= n; i++) 
		if (sccno[i] == sccno[i + n] || sccno[i] == sccno[i + 2 * n] || sccno[i + n] == sccno[i + 2 * n])return false;
	return true;
}
int main() {
	int t;
	scanf("%d", &t);
	int ex = 0;
	while (t--) {
		memset(head, -1, sizeof(head));
		tot = 0;
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; i++) {
			scanf("%d", &b[i]);
			b[i]--;
		}
		bool flag=true;
		for (int u,v,k,i = 1; i <= m; i++) {
			scanf("%d%d%d", &u, &v, &k);
			if (k == 0) {  //u,v两局必须要出一样的
				if (b[u] != b[v]) {   //Bob的u,v两局出的不一样
					if (b[u] == 0 && b[v] == 1||b[u]==1&&b[v]==0) {  //Bob两局中,一局出石头,一局出布,所以Alice两局都只能出布,其他的都不能出,当第u局出石头或剪刀时,第u局必须出布;第v局同上
						add(u, u + n);
						add(u + 2 * n, u + n);
						add(v, v + n);
						add(v + 2 * n, v + n);
					}
					if (b[u] == 0 && b[v] == 2||b[u]==2&&b[v]==0) {
						add(u + n, u);
						add(u + 2 * n, u);
						add(v + n, v);
						add(v + 2 * n, v);
					}
					if (b[u] == 1 && b[v] == 2 || b[u] == 2 && b[v] == 1) {
						add(u, u + 2 * n);
						add(u + n, u + 2 * n);
						add(v, v + 2 * n);
						add(v + n, v + 2 * n);
					}
				}
				else {  //当Bob的u,v两局出的相同时
					add(u + n * b[u], v + n * b[u]);  //Alice两局都出跟Bob一样的
					add(u + n*((b[u] + 1) % 3), v + n*((b[u] + 1) % 3));  //Alice两局都出能打败Bob的
				}
			}
			else if (k == 1) {  //Alice的u,v两局必须出不一样的
				if (b[u] == b[v]) {  //当Bob的u,v两局出的一样的,Alice的u,v两局必须一局出与Bob出一样的,另一局出能打败Bob的
					add(u + b[u] * n, v + n * ((b[u] + 1) % 3));
					add(u + n * ((b[u] + 1) % 3), v + b[u] * n);
					add(v + b[u] * n, u + n * ((b[u] + 1) % 3));
					add(v + n * ((b[u] + 1) % 3), u + b[u] * n);
				}
				else {  //Bob的两局出的不一样,分类讨论
					if (b[u] == 0 && b[v] == 1) {
						add(u + n, v + 2 * n);
						add(v + n, u);
					}
					if (b[u] == 0 && b[v] == 2) {
						add(u, v + 2 * n);
						add(v, u + n);
					}
					if (b[u] == 1 && b[v] == 2) {
						add(u + 2 * n, v);
						add(v + 2 * n, u + n);
					}
					if (b[u] == 1 && b[v] == 0) {
						add(u + n, v);
						add(v + n, u + 2 * n);
					}
					if (b[u] == 2 && b[v] == 0) {
						add(u, v + n);
						add(v, u + 2 * n);
					}
					if (b[u] == 2 && b[v] == 1) {
						add(u + 2 * n, v + n);
						add(v + 2 * n, u);
					}
				}
			}
		}
		printf("Case #%d: ", ++ex);
		printf(find_scc() ? "yes\n" : "no\n");
	}
	return 0;
}

你可能感兴趣的:(HDU 4115 Eliminate the Conflict (2-SAT判断))