Hdu1814 Peaceful Commission 【2-SAT、最小化答案字典序】

Peaceful Commission

题意

委员会由若干议员组成,且必须满足:

  1. 对于每个党派,在委员会中恰好 1 1 1 名代表(每个党派恰好 2 2 2 个代表)
  2. 如果两名议员互不喜欢,他们不能同时加入委员会

决定是否能组成委员会,如果可以,输出字典序最小的方式

思路

按照 2 − S A T 2-SAT 2SAT 的方式建图,要求最小化答案字典序的话,我们可以先试一下反变量(编号更小的代表)可不可行,如果不可行,再试一下原变量,如果两个都不行(在同一个 S C C SCC SCC 中),那么显然无解

d f s dfs dfs 的过程中,用一个来保存当前这一轮访问过的点,以便回溯撤销 v i s vis vis 数组

这样子操作的话,每个连通块中的点最多只会访问两次,复杂度应该是线性的

// Problem: Peaceful Commission
// Contest: HDOJ
// URL: https://acm.hdu.edu.cn/showproblem.php?pid=1814
// Memory Limit: 32 MB
// Time Limit: 10000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
#define fore(i,l,r)	for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n' 
#define ull unsigned long long

const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;

typedef long long ll;

std::vector<std::vector<int>> g;
std::vector<bool> vis;
int n, m;
std::stack<int> stk;

bool dfs(int u){
	if(vis[u ^ 1])	return false;
	if(vis[u])	return true;
	stk.push(u);
	vis[u] = true;
	for(auto v : g[u])
		if(!dfs(v))
			return false;
	return true;
}

void solve(){
	g.assign(2 * n + 1, std::vector<int>());
	vis.assign(2 * n + 1, false);
	fore(i, 0, m){
		int u, v;
		std::cin >> u >> v;
		--u;
		--v;
		g[u].push_back(v ^ 1);
		g[v].push_back(u ^ 1);
	}
	
	fore(i, 0, n){
		if(vis[i << 1] || vis[i << 1 | 1])	continue;
		while(!stk.empty())	stk.pop(); //这里必须清空,否则虽然不连通
		//但是会影响最终答案,因为原本true的变量设成了false,导致cout出错
		if(!dfs(i << 1)){
			while(!stk.empty()){ //选小的那个不行
				vis[stk.top()] = false;
				stk.pop();
			}
			if(!dfs(i << 1 | 1)){ //选大的也不行
				std::cout << "NIE\n";
				return;
			}
		}
	}
	
	fore(i, 0, n){
		std::cout << (vis[2 * i] ? 2 * i + 1 : 2 * i + 2) << endl;
	}
}

int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr); 
    while(std::cin >> n >> m){
    	solve();
    }
	return 0; 
}

/*
https://acm.hdu.edu.cn/showproblem.php?pid=1814
*/

/*
https://vjudge.net.cn/problem/HDU-1814
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值