整理的算法模板合集: ACM模板


注意一个坑,2SAT问题中如果要求你输出方案,如果你的代码输出的跟样例不一样,不要着急,因为2SAT 问题本来就是有多解,结果我样例不过,交上去就A了

方案输出时,color[x]谁小选谁

有 n 个布尔变量 x1∼xnx_1\sim x_nx1​∼xn​​,另有 m 个需要满足的条件,每个条件的形式都是 「x_i为 true / false 或 x_j​为 true / false」。比如 「x_1为真或 x_3为假」、「x_7​为假或 x_2为假」。2-SAT 问题的目标是给每个变量赋值使得所有条件得到满足。

2 - SAT模板

我们在2-sat问题中会得到若干个关系式形如:
p∨q,也就是p||q,p或q,p为true或者q为true

而我们离散数学中学逻辑关系式中有这么一个转化关系:
p∨q == ¬p → q ∧ ¬q → p

也就是:

p || q == -p ->q && -q -> p

根据p和q 的真假性我们可以得到下列表格

原关系式 建图方式
p∨qp \vee qp∨q ¬p→q∧¬q→p\neg p\rightarrow q\wedge\neg q\rightarrow p¬p→q∧¬q→p
¬p∨q\neg p\vee q¬p∨q p→q∧¬q→¬pp\rightarrow q\wedge\neg q\rightarrow\neg pp→q∧¬q→¬p
p∨¬qp\vee \neg qp∨¬q ¬p→¬q∧q→p\neg p\rightarrow \neg q\wedge q\rightarrow p¬p→¬q∧q→p
¬p∨¬q\neg p\vee\neg q\space \space¬p∨¬q   p→¬q∧q→¬pp\rightarrow\neg q\wedge q\rightarrow\neg pp→¬q∧q→¬p

我们按照箭头建有向边,构成一个有向图。

每一条有向边,u->v表示如果选择u,那么v也必须选择,不然就违反了关系式。

因此我们对这张图求强连通分量

那么,对于这张图中的每个强连通分量中的点一定要么同时选,要么同时不选。

判断无解:如果xi,0x_{i,0}xi,0​和xi,1x_{i,1}xi,1​在同一个强连通分量中,那么明显无解,因为二者对立不可能同时存在(xi,0x_{i,0}xi,0​ === 111 ~ nnn 指的是编号为i的点选择false,xi,1x_{i,1}xi,1​=== n+1n +1n+1 ~ 2∗n2*n2∗n指的是编号为i 的点选择true

这张拓扑图中,如果u可以到达v,那么u选择则v也必须选择。

tarjan算法 dfsdfsdfs 式地走一遍就是有向图的拓扑序,因为是用的栈存的节点,所以是拓扑逆序(对于每一个强连通分量都是一个拓扑逆序,块间也是拓扑序,因为每一块之间至多只有一条边,多了就连一块了)。

选择方案:对于每一个点来说,在xi,0x_{i,0}xi,0​和xi,1x_{i,1}xi,1​中选择拓扑序较大的点。这样就可以避免产生冲突了。并且这种方法一定可以构造出解。

当 xxx 所在的强连通分量的拓扑序在 ¬x\neg x¬x所在的强连通分量的拓扑序之后取 xxx 为真 就可以了。在使用 Tarjan 算法缩点找强连通分量的过程中,已经为每组强连通分量标记好顺序了——不过是反着的拓扑序。所以一定要写成 color[x] < color[-x]

其中我们一般在2-sat问题中用i表示false,用i+n表示true

时间复杂度为:O(n+m)O(n+m)O(n+m)

//时间复杂度O(n+m)
//当 x 所在的强连通分量的拓扑序在 ¬x 所在的强连通分量的拓扑序之后取 x
//为真 ,注意我们得到的是拓扑逆序,所以要写成color[x] < color[¬x]
// 其中 i 表示 ¬x,用 i+n 表示 x
p ∨ q == ¬p → q  ∧ ¬q → p
p || q == -p ->q && -q -> p
typedef long long ll;const int N = 2000007, M = 5000007, INF = 0x3f3f3f3f;int n, m;
int dfn[N], low[N], num;
bool vis[N], ins[N];
int a[N], scc_cnt;
int scc_id[N], color[N];
int stk[N], top;
int ver[M], nex[M], head[N], tot;void add(int x, int y){ver[tot] = y;nex[tot] = head[x];head[x] = tot ++ ;
}void tarjan(int x)
{dfn[x] = low[x] = ++ num;stk[++ top] = x;ins[x] = true;for(int i = head[x]; ~i; i = nex[i]){int y = ver[i];if(!dfn[y]){tarjan(y);low[x] = min(low[x], low[y]);}else if(ins[y])low[x] = min(low[x], dfn[y]);}if(low[x] == dfn[x]){int y;++ scc_cnt;do{y = stk[top -- ];ins[y] = false;scc_id[y] = scc_cnt;color[y] = scc_cnt;}while(x != y);}
}int main()
{memset(head, -1, sizeof head);scanf("%d%d", &n, &m);for(int i = 1; i <= m; ++ i){int p, q ,c ,d;scanf("%d%d%d%d", &p, &c, &q, &d);if(c && d){// p 且 q  -p-> q && -q -> padd(p, q + n);add(q, p + n);}else if(!c && d){//p -> q && -q -> -padd(p + n, q + n);add(q, p);}else if(c && !d){//-p -> -q && q -> padd(p, q);add(q + n, p + n);}else if(!c && !d){//p -> -q && q -> -padd(p + n, q);add(q + n, p);}}for(int i = 1; i <= 2 * n; ++ i){if(!dfn[i])tarjan(i);}for(int i = 1 ;i <= n; ++ i) {if(color[i] == color[i + n]){puts("IMPOSSIBLE");return 0;}}puts("POSSIBLE");//谁小选谁,这里i + n表示的是x (true)for(int i = 1; i <= n; ++ i)printf("%d ", (color[i] > color[i + n]));puts("");return 0;
}

位运算版

 g[p + n * c].push_back(q + n * (d ^ 1));g[q + n * d].push_back(p + n * (c ^ 1));

最小字典序解

题意:给你n个组,m条规则,每组有俩个人,这两个人不能同时出现,然后m条规则代表着有两个人,这两个人也不能同时出现,问你是否存在每组都能出现一人的选择方案

解题思路:因为这个需要字典序输出,所以只能用暴力的方法解决,如果x,y在同一条规则里面,那么建立一条边由x指向和y同一组的另一个人,y也这样做,然后开始暴力dfs

注意此模版求的就是最小字典序解(因为本题没有special judge…)

#define ll double
#define eps 1e-5using namespace std;inline ll Max(ll a,ll b){return a>b?a:b;}
inline ll Min(ll a,ll b){return a<b?a:b;}#define N 8010*2
#define M 40000+5struct Edge{int to, nex;
}edge[M];int head[N], edgenum;
void addedge(int u, int v){Edge E = {v, head[u]};edge[edgenum] = E;head[u] = edgenum ++;
}bool mark[N];
int Stack[N], top;
void init(){memset(head, -1, sizeof(head)); edgenum = 0;memset(mark, 0, sizeof(mark));
}bool dfs(int x){if(mark[x^1])return false;//一定是拆点的点先判断if(mark[x])return true;mark[x] = true;Stack[top++] = x;for(int i = head[x]; i != -1; i = edge[i].nex)if(!dfs(edge[i].to)) return false;return true;
}bool solve(int n){for(int i = 0; i < n; i+=2)if(!mark[i] && !mark[i^1]){top = 0;if(!dfs(i))//dfs(i) 假设i成立 {//当i不成立时,把所有因i成立的点都取消标记while( top ) mark[ Stack[--top] ] = false;if(!dfs(i^1))return false;//若i的对立面也不成立则i点无解}}return true;
}int main(){int n, i, j, m;while(~scanf("%d%d",&n,&m)){n <<= 1;init();while(m--){int u,v;scanf("%d %d",&u,&v); u--, v--;addedge(u,v^1);addedge(v,u^1);}if(solve(n)){for(i=0;i<n;i++)if(mark[i])printf("%d\n",i+1);}elseprintf("NIE\n");}return 0;
}

需要注意的是,如果题目中有重边的话,使用链式前向星就会导致RE,改成vector即可
如下题

2 - SAT + 二分答案


/*ACM-ICPC 2004 Europe - Southwestern) Now or later*/
typedef long long ll;
const int N = 5000007, M = 5000007, INF = 0x3f3f3f3f;int n, m;
int dfn[N], low[N], num;
int head[N], ver[M], nex[M], tot;
vector<int>g[N];
int a[N][2];
int stk[N], top, scc_cnt;
bool ins[N];
int color[N];void tarjan(int x)
{dfn[x] = low[x] = ++ num;stk[++ top] = x;ins[x] = true;for(size_t i = 0; i < g[x].size(); ++ i){int y = g[x][i];if(!dfn[y]){tarjan(y);low[x] = min(low[x], low[y]);}else if(ins[y])low[x] = min(low[x], dfn[y]);}if(low[x] == dfn[x]){int y;++ scc_cnt;color[x] = scc_cnt;do{y = stk[top -- ];ins[y] = false;color[y] = scc_cnt;}while(x != y);}return ;
}inline bool check(int x){//memset(head, -1, sizeof head);for(int i = 1; i <= 2 * n; ++ i)g[i].clear();memset(dfn, 0, sizeof dfn);memset(low, 0, sizeof low);memset(color, 0, sizeof color);memset(ins, 0, sizeof ins);tot = num = scc_cnt = 0;//0 : -x : E 早 : i//1 : x : L 晚 : i + n//比较之后的两个相差的时间是否小于x//如果小于x的话那么两者就不能同时出现(同时为true)for(int p = 1;p <= n;++ p){for(int c = 0; c <= 1; ++ c){for(int q = p + 1; q <= n; ++ q){for(int d = 0; d <= 1; ++ d){if(abs(a[p][c] - a[q][d]) < x){/*if(c && d){//11 -> 01, 01add(p, q + n);add(q, p + n);}else if(!c && d){//01 -> 11, 00add(p + n, q + n);add(q, p);}else if(c && !d){//10 -> 00, 11add(p, q);add(q + n, p + n);}else if(!c && !d){//00 -> 10, 01add(p + n, q);add(q + n, p);}*/g[p + n * c].push_back(q + n * (d ^ 1));g[q + n * d].push_back(p + n * (c ^ 1));}}}}}for(int i = 1; i <= 2 * n; ++ i)if(!dfn[i])tarjan(i);for(int i = 1; i <= n; ++ i){if(color[i] == color[i + n])return false;}return true;
}void solve(){int l = 0, r = 0, ans = -1;for(int i =1; i <= n; ++ i)scanf("%d%d", &a[i][0], &a[i][1]), r = max(max(a[i][1], a[i][0]), r);while(l <= r){int mid = l + r >> 1;if(check(mid))ans = mid, l = mid + 1;else r = mid - 1;}printf("%d\n", ans);return ;
}int main()
{while(scanf("%d", &n) != EOF){solve();}return 0;
}

模板 - 2 - SAT问题相关推荐

  1. Spring JDBC-NamedParameterJdbcTemplate模板类

    概述 示例 BeanPropertySqlParameterSource 使用示例 MapSqlParameterSource使用示例 NamedParameterJdbcTemplate 支持 in ...

  2. HDU 3062 Party(2-sat题模板+tarjan )

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

  3. 模板:2-SAT问题

    文章目录 前言 实现 代码 所谓2-SAT,就是解决两个SAT的问题 (逃) 前言 SAT 是适定性(Satisfiability)问题的简称.一般形式为 k - 适定性问题,简称 k-SAT.而当 ...

  4. 子模板继承父模板示例_模板设计模式示例

    子模板继承父模板示例 本文是我们名为" Java设计模式 "的学院课程的一部分. 在本课程中,您将深入研究大量的设计模式,并了解如何在Java中实现和利用它们. 您将了解模式如此重 ...

  5. python小程序模板——阿龙的小百宝箱

    requests模板 import requests url='' params={} response=requests.get(url,params).text print(response) w ...

  6. 23_字符串的格式化_format 函数_% 占位符 _ f-string _Template _模板字符等

    文章目录 23.字符串的格式化 Resource `format()` 讲解 replacement_field 语法说明 format_spec 语法说明 符号系统 分组选项(关于整个的语法 如何对 ...

  7. 【2-SAT初学+模板题讲解】POJ3683 Priest John's Busiest Day

    什么是2-SAT? SAT是适定性(Satisfiability)问题的简称 .一般形式为k-适定性问题,简称 k-SAT. 可以证明,当k>2时,k-SAT是NP完全的.因此一般讨论的是k=2 ...

  8. 我的所有优质博客全部开源啦(我自己原创的《ACM模板》《算法全家桶》《算法竞赛中的初等数论》 PDF免费下载)

    你好呀ヾ(≧▽≦*)o 我是繁凡さん 这两年来我写了很多长篇文章,主要涉及数据结构,算法,程序设计竞赛,数学,计算几何等方面的内容: <数据结构>C语言版(清华严蔚敏考研版) 全书知识梳理 ...

  9. SAT OG 写作辅导:良知是一种比金钱名望和权力更有力的激励?

    SAT是由美国大学委员会(College Board主办,SAT成绩是世界各国高中生申请美国名校学习及奖学金的重要参考.总分2400分:分为阅读.写作.数学.理事会(College Board.ETS ...

最新文章

  1. .net core 微服务通讯组件Orleans的使用与配置
  2. Spring Boot中使用JSP
  3. C++ 强制类型转换(转载)
  4. NFS 服务器配置说明
  5. 【渝粤题库】陕西师范大学210004幼儿园美术教育作业(高起专)
  6. oracle优质图书,经典Oracle图书推荐(之四)_oracle
  7. 漫画:IT公司年终总结会开崩了...
  8. C++ OpenCV光平面标定-线激光提取
  9. cad显示有点暗_CAD参照的图框颜色太暗怎么调整? 一打开CAD就出现这样的情况怎么办...
  10. 【转】当我们说“区块链是无需信任的”,我们的意思是
  11. 基于DPCA的线性监督分类的故障诊断方法-T2和SPE统计量的计算
  12. java ppt 绘图,PPT图片别再直接插入,这样处理一下,让你的PPT秒变高逼格
  13. 计算机系统安全期末复习
  14. 单模、多模有什么区别
  15. Android-PickerView系列之封装篇(三)
  16. python2.7常用的标准库
  17. 【学习笔记】:Multi-mode Transformer Transducer with Stochastic Future Context
  18. hmc如何进入aix系统_HMC配置及操作
  19. 光伏产业 | 我国光伏银浆正逐步国产化替代,已打破国际垄断局面
  20. 小米4的拆机教程,更换屏幕,更换主板等

热门文章

  1. Python 图像处理简介——色彩阴影调整
  2. 给大家分享一个私藏已久的Python神器!
  3. 本科生去面试算法工程师心酸的故事
  4. iOS开发者知识普及,Swift 挑战 Objective-C,谁会笑到最后?...
  5. windows 常用thread方法
  6. 嵌入式定制开发合作伙伴应该这样选择
  7. php的yii2框架下开发环境xampp,vim,xdebug,DBGp的搭建
  8. html+css3实现二级下拉菜单
  9. 转:项目报告应该汇报什么内容?
  10. C# 获得系统AppData路径