SAT——Satisfiability
2-SAT问题介绍:
一串布尔变量,每个变量只能为真或假。要求对这些变量进行赋值,满足布尔方程。
例题:
1.P4782 【模板】2-SAT 问题
点击查看代码块
/*
-代表非的意思,n为点(变量)的个数
a或b—————— -a->b 且 -b->a 对应加边:(a+n,b),(b+n,a)
-a或b—————— a->b 且 -b->-a 对应加边:(a,b),(b+n,a+n)
a或-b—————— -a->-b 且 b->a 对应加边:(a+n,b+n),(b,a)
-a或-b—————— a->-b 且 b->-a 对应加边:(a,b+n),(b,a+n)
*/
#include <bits/stdc++.h>
#define ed end()
#define bg begin()
#define mkp make_pair
#define pb push_back
#define v(T) vector<T>
#define all(x) x.bg,x.ed
#define newline puts("")
#define si(x) ((int)x.size())
#define rep(i,n) for(int i=1;i<=n;++i)
#define rrep(i,n) for(int i=0;i<n;++i)
#define srep(i,s,t) for(int i=s;i<=t;++i)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2e6+10;
const int inf = 0x7f7f7f7f;
const ll inf_ll = 1ll*inf*inf;
const int Mod = 1e9+7;
const double eps = 1e-7;
int n,m;
int head[maxn],cnt=0;
struct edge{
int v,next;
}e[maxn<<1];
void add(int u,int v){
e[cnt].v=v;e[cnt].next=head[u];head[u]=cnt++;
}
int dfn[maxn],low[maxn];
bool instack[maxn];
stack<int> st;
int ct = 0,book[maxn],num[maxn],tot = 0;
void tarjan(int u){
low[u]=dfn[u]=++tot;
instack[u]=1;
st.push(u);
for (int i=head[u];~i;i=e[i].next){
int v=e[i].v;
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]){
++ct;
while(1){
int x=st.top();
st.pop();
book[x]=ct;
num[ct]++;
instack[x]=0;
if(x == u) break;
}
}
}
int main(){
memset(head,-1,sizeof(head));
cnt=0;
cin>>n>>m;
for (int i=1;i<=m;i++){
int ii,a,jj,b;
scanf("%d%d%d%d",&ii,&a,&jj,&b);
if(a && b){//a或b
add(ii+n,jj);add(jj+n,ii);
}
else if(!a && b){//-a或b
add(ii,jj);add(jj+n,ii+n);
}
else if(a && !b){//a或b
add(ii+n,jj+n);add(jj,ii);
}
else if(!a && !b){
add(ii,jj+n);add(jj,ii+n);
}
}
// for (int i=1;i<=m;i++){
// int ii,a,jj,b;
// scanf("%d%d%d%d",&ii,&a,&jj,&b);
// add(ii+n*(a&1),jj+n*(b^1));
// add(jj+n*(b&1),ii+n*(a^1));
// }
for (int i=1;i<=2*n;i++){
if(!dfn[i]) tarjan(i);
}
// for (int i=1;i<=2*n;i++){
// printf("i = %d book = %d
",i,book[i]);
// }
for (int i = 1; i <= n; ++i)
if (book[i] == book[i + n]) { // x 与 -x 在同一强连通分量内,一定无解
puts("IMPOSSIBLE");
return 0;
}
puts("POSSIBLE");
for (int i = 1; i <= n; ++i){
if(book[i] < book[i + n]){
printf("1 "); // 如果不使用 Tarjan 找环,改成大于号
}
else printf("0 ");
}
newline;
return 0;
}
/*
3 1
1 1 3 0
*/
点击查看代码块
/*
我们设满式菜品m为1,汉式菜品h为0,
*/
#include <bits/stdc++.h>
#define ed end()
#define bg begin()
#define mkp make_pair
#define pb push_back
#define v(T) vector<T>
#define all(x) x.bg,x.ed
#define newline puts("")
#define si(x) ((int)x.size())
#define rep(i,n) for(int i=1;i<=n;++i)
#define rrep(i,n) for(int i=0;i<n;++i)
#define srep(i,s,t) for(int i=s;i<=t;++i)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2e4+10;
const int inf = 0x7f7f7f7f;
const ll inf_ll = 1ll*inf*inf;
const int Mod = 1e9+7;
const double eps = 1e-7;
int n,m;
int head[maxn],cnt=0;
struct edge{
int v,next;
}e[maxn<<1];
void add(int u,int v){
e[cnt].v=v;e[cnt].next=head[u];head[u]=cnt++;
}
int dfn[maxn],low[maxn],tot,ct,book[maxn];
int instack[maxn];
stack<int> st;
int num = 0;
void init(){
cnt=0;
memset(head,-1,sizeof(head));
num=tot=ct=0;
for (int i=1;i<=2*n;i++){
low[i]=dfn[i]=book[i]=instack[i]=0;
}
}
void tarjan(int u){
dfn[u]=low[u]=++tot;
instack[u]=1;
st.push(u);
for (int i=head[u];~i;i=e[i].next){
int v=e[i].v;
if(!dfn[v]) {
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(instack[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]){
++ct;
while(1){
int x=st.top();
st.pop();
book[x]=ct;
instack[x]=0;
if(x==u) break;
}
}
}
int main(){
int T;
scanf("%d",&T);
for(int _=1;_<=T;_++)
{
while(!st.empty()) st.pop();
scanf("%d%d",&n,&m);
init();
for (int i=1;i<=m;i++){
string s1,s2;
cin>>s1>>s2;
int u=0,v=0;
for (int j=1;j<s1.size();j++){
u=u*10+s1[j]-'0';
}
for (int j=1;j<s2.size();j++){
v=v*10+s2[j]-'0';
}
// cout<<"u = "<<u<<" v = "<<v<<endl;
if(s1[0]=='m' && s2[0]=='m'){
add(u+n,v),add(v+n,u);
}
else if(s1[0]=='m' && s2[0]=='h'){
add(u+n,v+n),add(v,u);
}
else if(s1[0]=='h' && s2[0]=='m'){
add(u,v),add(v+n,u+n);
}
else if(s1[0]=='h' && s2[0]=='h'){
add(u,v+n),add(v,u+n);
}
}
for (int i=1;i<=(n*2);i++){
if(!dfn[i]) tarjan(i);
}
int flag = 1;
for (int i=1;i<=n;i++){
if(book[i]==book[i+n]){
puts("BAD");
flag = 0;
break;
}
}
if(flag) puts("GOOD");
}
return 0;
}
3.P6378 [PA2010]Riddle
题意:
n 个点 m 条边的无向图被分成 k 个部分。每个部分包含一些点。
请选择一些关键点,使得每个部分恰有一个关键点,且每条边至少有一个端点是关键点。
点击查看代码块
/*
两限制:
每个部分中只能有一个关键点,每条边的两端必须有一个关键点
优化点集内建边算法
我们设prei表示该部分中前i个点(包括第i个)是否为关键点,
则有:
ai->prei, -prei->-ai
pre(i-1)->prei, -prei->-pre(i-1)
pre(i-1)->-ai, ai->-pre(i-1)
prei表示为ai+2*n, -prei表示为ai+3*n
*/
#include <bits/stdc++.h>
#define ed end()
#define bg begin()
#define mkp make_pair
#define pb push_back
#define v(T) vector<T>
#define all(x) x.bg,x.ed
#define newline puts("")
#define si(x) ((int)x.size())
#define rep(i,n) for(int i=1;i<=n;++i)
#define rrep(i,n) for(int i=0;i<n;++i)
#define srep(i,s,t) for(int i=s;i<=t;++i)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2e6+10;
const int inf = 0x7f7f7f7f;
const ll inf_ll = 1ll*inf*inf;
const int Mod = 1e9+7;
const double eps = 1e-7;
int n,m,k;
int head[maxn<<2],cnt=0;
struct edge{
int v,next;
}e[maxn<<4];
void add(int u,int v){
e[cnt].v=v;
e[cnt].next=head[u];
head[u]=cnt++;
}
int pre[maxn<<1];
int dfn[maxn<<2],low[maxn<<2],instack[maxn<<2],book[maxn<<2];
int tot=0,ct=0;
stack<int> st;
void tarjan(int u){
dfn[u]=low[u]=++tot;
instack[u]=1;
st.push(u);
for (int i=head[u];~i;i=e[i].next){
int v=e[i].v;
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]){
++ct;
while(1){
int x=st.top();
st.pop();
instack[x]=0;
book[x]=ct;
if(x == u) break;
}
}
}
bool check(){
for (int i=1;i<=n;i++){
if(book[i] == book[i+n] || book[i+2*n] == book[i+3*n]) return false;
}
return true;
}
int main(){
memset(head,-1,sizeof(head));
cnt=0;
cin>>n>>m>>k;
for (int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u+n,v),add(v+n,u);
}
for (int i=1;i<=k;i++){
int w;
scanf("%d",&w);
for (int j=1;j<=w;j++){
scanf("%d",&pre[j]);
add(pre[j],pre[j]+2*n),add(pre[j]+3*n,pre[j]+n);
}
for (int j=2;j<=w;j++){
add(pre[j-1]+2*n,pre[j]+2*n),add(pre[j]+3*n,pre[j-1]+3*n);
add(pre[j-1]+2*n,pre[j]+n),add(pre[j],pre[j-1]+3*n);
}
}
for (int i=1;i<=(n<<2);i++){
if(!dfn[i]) tarjan(i);
}
if(check()) puts("TAK");
else puts("NIE");
return 0;
}
4.TV Show Game
题意:
给定若干个人,每个人三次猜测某些灯的颜色(红色或者蓝色),如果一个人的三次猜测中有两个或两个以上是正确的,
则这个人能获得一个礼物。
问是否存在一种灯的颜色组合使得所有人都能获得一个礼物
点击查看代码块
/*
给定若干个人,每个人三次猜测某些灯的颜色(红色或者蓝色),如果一个人的三次猜测中有两个或两个以上是正确的,
则这个人能获得一个礼物。
问是否存在一种灯的颜色组合使得所有人都能获得一个礼物
RRR R表示1,B表示0(7~0)
RRB
RBR
RBB
BRR
BRB
BBR
BBB
*/
#include <bits/stdc++.h>
#define ed end()
#define bg begin()
#define mkp make_pair
#define pb push_back
#define v(T) vector<T>
#define all(x) x.bg,x.ed
#define newline puts("")
#define si(x) ((int)x.size())
#define rep(i,n) for(int i=1;i<=n;++i)
#define rrep(i,n) for(int i=0;i<n;++i)
#define srep(i,s,t) for(int i=s;i<=t;++i)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 1e5+10;
const int inf = 0x7f7f7f7f;
const ll inf_ll = 1ll*inf*inf;
const int Mod = 1e9+7;
const double eps = 1e-7;
int head[maxn],cnt=0;
struct edge{
int v,next;
}e[maxn<<4];
void add(int u,int v){
e[cnt].v=v;e[cnt].next=head[u];head[u]=cnt++;
}
void init(){
memset(head,-1,sizeof(head));cnt=0;
}
int n,m;
int dfn[maxn<<1],low[maxn],instack[maxn],book[maxn];
int tot=0,ct=0;
stack<int>st;
void tarjan(int u){
dfn[u]=low[u]=++tot;
instack[u]=1;
st.push(u);
for (int i=head[u];~i;i=e[i].next){
int v=e[i].v;
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]){
++ct;
while(1){
int x=st.top();st.pop();
book[x]=ct;
instack[x]=0;
if(x == u) break;
}
}
}
int check(char a,char b,char c){
return (((a=='R')<<2)+((b=='R')<<1)+(c=='R'));
}
int main(){
// cout<<check('R','R','R')<<endl;
init();
cin>>n>>m;
int a,b,c;
char ca,cb,cc;
for (int i=1;i<=m;i++){
cin>>a>>ca>>b>>cb>>c>>cc;
int op = check(ca,cb,cc);
if(7 == op){//RRR
add(a+n,b),add(a+n,c);
add(b+n,a),add(b+n,c);
add(c+n,a),add(c+n,b);
}
else if(6 == op){//RRB
add(a+n,b),add(a+n,c+n);
add(b+n,a),add(b+n,c+n);
add(c,a),add(c,b);
}
else if(5 == op){//RBR
add(a+n,b+n),add(a+n,c);
add(b,a),add(b,c);
add(c+n,a),add(c+n,b+n);
}
else if(4 == op){//RBB
add(a+n,b+n),add(a+n,c+n);
add(b,a),add(b,c+n);
add(c,a),add(c,b+n);
}
else if(3 == op){//BRR
add(a,b),add(a,c);
add(b+n,a+n),add(b+n,c);
add(c+n,a+n),add(c+n,b);
}
else if(2 == op){//BRB
add(a,b),add(a,c+n);
add(b+n,a+n),add(b+n,c+n);
add(c,a+n),add(c,b);
}
else if(1 == op){//BBR
add(a,b+n),add(a,c);
add(b,a+n),add(b,c);
add(c+n,a+n),add(c+n,b+n);
}
else if(0 == op){//BBB
add(a,b+n),add(a,c+n);
add(b,a+n),add(b,c+n);
add(c,a+n),add(c,b+n);
}
}
for (int i=1;i<=2*n;i++){
if(!dfn[i]) tarjan(i);
}
for (int i=1;i<=n;i++){
if(book[i] == book[i+n]){
puts("-1");
return 0;
}
}
for (int i=1;i<=n;i++){
if(book[i]<book[i+n]){
printf("R");
}
else printf("B");
}
newline;
return 0;
}
#include <bits/stdc++.h>
#define mkp make_pair
#define pb push_back
#define v(T) vector<T>
#define all(x) x.bg,x.ed
#define newline puts("")
#define si(x) ((int)x.size())
#define rep(i,n) for(int i=1;i<=n;++i)
#define rrep(i,n) for(int i=0;i<n;++i)
#define srep(i,s,t) for(int i=s;i<=t;++i)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 5e5+10;
const int inf = 0x7f7f7f7f;
const int Mod = 1e9+7;
const double eps = 1e-7;
int n,m,d;
int x[maxn],y[maxn];
char c1[maxn],c2[maxn],c[maxn];
int pos[maxn],k = 0;//x的位置
int head[maxn],cnt=0;
struct edge{
int v,next;
}e[maxn<<1];
void add(int u,int v){
e[cnt].v=v;e[cnt].next=head[u];head[u]=cnt++;
}
int dfn[maxn],book[maxn],low[maxn];
bool instack[maxn];
int tot,ct;
stack<int> st;
void init(){
memset(e,0,sizeof(e));
for (int i=0;i<maxn;i++){
head[i]=-1;
dfn[i]=low[i]=book[i]=0;
instack[i]=0;
}
while(!st.empty()) st.pop();
tot=ct=cnt=0;
}
void tarjan(int u){
dfn[u]=low[u]=++tot;
instack[u]=1;
st.push(u);
for (int i=head[u];~i;i=e[i].next){
int v=e[i].v;
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]){
++ct;
while(1){
int x=st.top();
st.pop();
instack[x]=0;
book[x]=ct;
if(u == x) break;
}
}
}
char Ans[maxn];
bool check(){
for (int i=1;i<=2*n;i++){
if(!dfn[i]) tarjan(i);
}
for (int i=1;i<=n;i++){
if(book[i] == book[i+n]) return false;
if(book[i]<book[i+n]){
Ans[i] = (c[i]=='A')?'B':'A';
}
else Ans[i] = (c[i]=='C')?'B':'C';
}
for (int i=1;i<=n;i++){
printf("%c",Ans[i]);
}
return true;
}
void solve(){
for (int i=0;i<(1<<d);i++){//枚举2的d次方
init();
for (int j=1;j<=d;j++){
c[pos[j]]=(i & (1<<(j-1))) ? 'A' : 'B';//pos[j]表示第j个x地图的位置,判断每个地图的情况
}
for (int j=1;j<=m;j++){
if(c1[j] == c[x[j]]) continue;//如果第i场比赛不能用hi,显然关系无法成立
if(c2[j] == c[y[j]]) {
if(c1[j] == 'C' || (c1[j] == 'B' && c[x[j]] == 'C'))
/*如果第jj场比赛不能用hj,那么这条关系不能成立,即i不能选hi,只能选择另一辆可行的车,
此时根据前面关系数组的建立,判断hi是第一辆还是第二辆可行的车,将其连到另一个(意为必须选另一个)*/
add(x[j] + n, x[j]);
else add(x[j], x[j] + n);
continue;
}
//关系成立需要建立(hi->hj),(`hj->`hi)
int add1 , add2;
if(c1[j] == 'C' || (c1[j] == 'B' && c[x[j]] == 'C')) add1 = n;
else add1 = 0;
if(c2[j] == 'C' || (c2[j] == 'B' && c[y[j]] == 'C')) add2 = n;
else add2 = 0;
add(x[j] + add1, y[j] + add2); //注意建正反两条边
add(y[j] - add2 + n, x[j] - add1 + n); //从正边到反边可以用-add + n 得到,可手模理解一下
}
if(check()) exit(0);
}
printf("-1");
return ;
}
int main(){
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
init();
cin>>n>>d;
cin>>(c+1);
cin>>m;
for (int i=1;i<=m;i++){
scanf("%d %c %d %c",&x[i],&c1[i],&y[i],&c2[i]);
}
for (int i=1;i<=n;i++){
c[i]-=32;
if(c[i] == 'X') pos[++k] = i;//记录地图x的位置
}
solve();
return 0;
}