tkmst201's Library

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub tkmst201/Library

:heavy_check_mark: 2-SAT
(Mathematics/TwoSat.hpp)

概要

論理変数 $x_0, \ldots, x_{N-1}$ に対して $(x_0 \lor x_1) \land (\lnot x_0 \lor x_2) \land (x_3 \lor x_0) \land (x_5) \land (x_2 \lor x_{N-1})$ のような形式の論理式を満たす真偽の組み合わせを求めます。
より具体的には、クロージャ(括弧) の積で表されていて各クロージャ内は 2 つ以下のリテラルの和で表されているような論理式を解きます。


コンストラクタ

TwoSat(int n)

$n$ 個の論理変数で初期化します。

制約

計算量



メンバ関数

以下、論理変数の個数を $N$ とし、それぞれ $x_0, \ldots, x_{N-1}$ とします。


int size()

論理変数の個数 $N$ を返します。

計算量


void add_clause(int x, bool xb, int y, bool yb)

クロージャ $x = xb \lor y = yb$ を追加します。

制約

計算量


void add_clause(int x, bool xb)

クロージャ $x = xb$ を追加します。

制約

計算量


bool build()

論理式を満たすような論理値の割り当てを求めます。 割り当てることができた場合は $true$ を、割り当てが存在しない場合は $false$ を返します。

計算量

const vector & ans_list

論理式を満たすような論理値の割り当てを返します。
$[i] := x_i$ の論理値

制約

計算量

bool get(int i)

論理式を満たすような論理値の割り当てで $x_i$ の真偽値を返します。

制約

計算量



使用例

#include <bits/stdc++.h>
#include "Mathematics/TwoSat.hpp"
using namespace std;

int main() {
	TwoSat sat(5);
	cout << "sat.size() = " << sat.size() << endl; // 5
	sat.add_clause(0, true, 1, false);
	sat.add_clause(1, false, 2, false);
	
	sat.build();
	// true true false true true
	for (bool b : sat.ans_list()) cout << boolalpha << b << " "; cout << '\n';
	for (int i = 0; i < sat.size(); ++i) cout << boolalpha << sat.get(i) << " "; cout << '\n';
	
	sat.add_clause(0, false);
	sat.add_clause(1, false);
	sat.build();
	// false false false true true
	for (bool b : sat.ans_list()) cout << boolalpha << b << " "; cout << '\n';
	
	sat.add_clause(0, true, 1, true);
	bool f = sat.build();
	cout << boolalpha << f << endl; // false
}


参考

2021/03/18: https://noshi91.hatenablog.com/entry/2019/10/03/184812
2021/03/18: https://www.slideshare.net/yoshiokatsuneo/2sat-72370606
2020/09/10: AC Library


Depends on

Verified with

Code

#ifndef INCLUDE_GUARD_TWO_SAT_HPP
#define INCLUDE_GUARD_TWO_SAT_HPP

#include "GraphTheory/StronglyConnectedComponents.hpp"

#include <vector>
#include <cassert>

/**
 * @brief https://tkmst201.github.io/Library/Mathematics/TwoSat.hpp
 */
struct TwoSat {
	using scc_type = StronglyConnectedComponents;
	
private:
	int n;
	typename scc_type::Graph g;
	std::vector<bool> ans;
	bool satisfiability = false;
	
public:
	explicit TwoSat(int n) : n(n), g(2 * n) {}
	
	int size() const noexcept {
		return n;
	}
	
	void add_clause(int x, bool xb, int y, bool yb) {
		assert(0 <= x && x < n);
		assert(0 <= y && y < n);
		g[x + (xb ? n : 0)].emplace_back(y + (yb ? 0 : n));
		g[y + (yb ? n : 0)].emplace_back(x + (xb ? 0 : n));
		satisfiability = false;
	}
	
	void add_clause(int x, bool xb) {
		assert(0 <= x && x < n);
		g[x + (xb ? n : 0)].emplace_back(x + (xb ? 0 : n));
		satisfiability = false;
	}
	
	bool build() {
		scc_type scc(g);
		ans.assign(n, false);
		satisfiability = false;
		for (int i = 0; i < n; ++i) {
			if (scc.rank(i) == scc.rank(i + n)) return false;
			if (scc.rank(i) > scc.rank(i + n)) ans[i] = true;
		}
		satisfiability = true;
		return true;
	}
	
	const std::vector<bool> & ans_list() const noexcept {
		assert(satisfiability);
		return ans;
	}
	
	bool get(int i) const noexcept {
		assert(satisfiability);
		assert(0 <= i && i < n);
		return ans[i];
	}
};

#endif // INCLUDE_GUARD_TWO_SAT_HPP
#line 1 "Mathematics/TwoSat.hpp"



#line 1 "GraphTheory/StronglyConnectedComponents.hpp"



#include <vector>
#include <cassert>
#include <algorithm>

/**
 * @brief https://tkmst201.github.io/Library/GraphTheory/StronglyConnectedComponents.hpp
 */
struct StronglyConnectedComponents {
	using Graph = std::vector<std::vector<int>>;
	
private:
	int n;
	std::vector<int> rank_;
	std::vector<std::vector<int>> rank_list_;
	
public:
	explicit StronglyConnectedComponents(const Graph & g) : n(g.size()) {
		Graph rg(n);
		for (int i = 0; i < n; ++i) {
			for (int j : g[i]) {
				assert(0 <= j && j < n);
				rg[j].emplace_back(i);
			}
		}
		std::vector<bool> visited(n, false);
		std::vector<int> num;
		auto dfs = [&](auto self, int u) -> void {
			visited[u] = true;
			for (int v : g[u]) if (!visited[v]) self(self, v);
			num.emplace_back(u);
		};
		for (int i = 0; i < n; ++i) if (!visited[i]) dfs(dfs, i);
		int cnt = 0;
		visited.assign(n, false);
		rank_.assign(n, -1);
		auto rdfs = [&](auto self, int u) -> void {
			visited[u] = true;
			rank_[u] = cnt;
			for (int v : rg[u]) if (!visited[v]) self(self, v);
		};
		for (int i = n - 1; i >= 0; --i) if (!visited[num[i]]) rdfs(rdfs, num[i]), ++cnt;
		rank_list_.assign(cnt, {});
		for (int i = 0; i < n; ++i) rank_list_[rank_[i]].emplace_back(i);
	}
	
	int size() const noexcept {
		return n;
	}
	
	int scc_size() const noexcept {
		return rank_list_.size();
	}
	
	int scc_size(int k) const noexcept {
		assert(0 <= k && k < scc_size());
		return rank_list_[k].size();
	}
	
	int rank(int u) const noexcept {
		assert(0 <= u && u < size());
		return rank_[u];
	}
	
	const std::vector<int> & rank_list(int k) const noexcept {
		assert(0 <= k && k < scc_size());
		return rank_list_[k];
	}
	
	Graph get_graph(const Graph & g) const {
		Graph res(scc_size());
		for (int i = 0; i < scc_size(); ++i) {
			for (int j : rank_list_[i]) for (int v : g[j]) if (rank(v) != i) res[i].emplace_back(rank(v));
			std::sort(begin(res[i]), end(res[i]));
			res[i].erase(unique(begin(res[i]), end(res[i])), end(res[i]));
		}
		return res;
	}
};


#line 5 "Mathematics/TwoSat.hpp"

#line 8 "Mathematics/TwoSat.hpp"

/**
 * @brief https://tkmst201.github.io/Library/Mathematics/TwoSat.hpp
 */
struct TwoSat {
	using scc_type = StronglyConnectedComponents;
	
private:
	int n;
	typename scc_type::Graph g;
	std::vector<bool> ans;
	bool satisfiability = false;
	
public:
	explicit TwoSat(int n) : n(n), g(2 * n) {}
	
	int size() const noexcept {
		return n;
	}
	
	void add_clause(int x, bool xb, int y, bool yb) {
		assert(0 <= x && x < n);
		assert(0 <= y && y < n);
		g[x + (xb ? n : 0)].emplace_back(y + (yb ? 0 : n));
		g[y + (yb ? n : 0)].emplace_back(x + (xb ? 0 : n));
		satisfiability = false;
	}
	
	void add_clause(int x, bool xb) {
		assert(0 <= x && x < n);
		g[x + (xb ? n : 0)].emplace_back(x + (xb ? 0 : n));
		satisfiability = false;
	}
	
	bool build() {
		scc_type scc(g);
		ans.assign(n, false);
		satisfiability = false;
		for (int i = 0; i < n; ++i) {
			if (scc.rank(i) == scc.rank(i + n)) return false;
			if (scc.rank(i) > scc.rank(i + n)) ans[i] = true;
		}
		satisfiability = true;
		return true;
	}
	
	const std::vector<bool> & ans_list() const noexcept {
		assert(satisfiability);
		return ans;
	}
	
	bool get(int i) const noexcept {
		assert(satisfiability);
		assert(0 <= i && i < n);
		return ans[i];
	}
};
Back to top page