【uoj#317】[NOI2017]游戏 2-SAT
给出 个赛车赛道和A、B、C三种赛车,除了 个赛道可以使用所有三种赛车以外每个都只能使用给出的两种之一。另外给出 条限制:某个赛道使用X则某另一个赛道必须使用Y。问:是否存在一种方案满足所有条件?输出一种合法方案。
。
题解
2-SAT
3-SAT是NP完全问题,由于 只有 ,因此考虑枚举每个万能位置的取值,转化为2-SAT问题。
那么对于一条限制,显然描述对应着一条边;另外一个命题的逆否命题,因此则有:第二个不用Y,第一个就不能用X,还要连这样的边(考场上没有想到对称边,还以为标算不是2-SAT)。
特殊情况:
第一个没有X,则无视这条边;
第二个没有Y,则第一个不能选X,第一个选X向不选X连边。
然后跑tarjan,对立点在同一强连通分量里则不成立,否则有解。缩点建新图跑拓扑排序,对立点中先排到的点不选,后排到的选。
这里有一个小trick:tarjan中强连通分量的编号顺序就是逆拓扑序(考虑tarjan的过程,挺好理解的),因此不用实际拓扑排序,直接比较对立点所属强连通分量编号即可,较小的那个是相应方案。
这样枚举万能位置的选择,每个位置有三种情况。考虑到枚举万能位置不能选什么,一次可以选出两种,只需要枚举两种情况。
时间复杂度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | #include <cstdio> #include <cstring> #include <algorithm> #define N 100010 using namespace std; int n , m , flag , px[N] , vx[N] , py[N] , vy[N] , head[N] , to[N << 1] , next[N << 1] , cnt , deep[N] , low[N] , tot , ins[N] , sta[N] , top , bl[N] , num; char str[N >> 1] , sx[3] , sy[3]; inline void add( int x , int y) { to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt; } inline int getid( int p , int v) { if (str[p] == 'a' ) return v == 0 ? 0 : p * 2 + v - 1; else if (str[p] == 'b' ) return v == 1 ? 0 : p * 2 + v / 2; else return v == 2 ? 0 : p * 2 + v; } void tarjan( int x) { int i; deep[x] = low[x] = ++tot , ins[x] = 1 , sta[++top] = x; for (i = head[x] ; i ; i = next[i]) { if (!deep[to[i]]) tarjan(to[i]) , low[x] = min(low[x] , low[to[i]]); else if (ins[to[i]]) low[x] = min(low[x] , deep[to[i]]); } if (deep[x] == low[x]) { int t; num ++ ; do { t = sta[top -- ]; ins[t] = 0 , bl[t] = num; } while (t != x); } } void solve() { int i , x , y; memset (head , 0 , sizeof (head)); memset (deep , 0 , sizeof (deep)); cnt = tot = top = num = 0; for (i = 1 ; i <= m ; i ++ ) { x = getid(px[i] , vx[i]) , y = getid(py[i] , vy[i]); if (x) { if (y) add(x , y) , add(y ^ 1 , x ^ 1); else add(x , x ^ 1); } } for (i = 2 ; i <= 2 * n + 1 ; i ++ ) if (!deep[i]) tarjan(i); for (i = 1 ; i <= n ; i ++ ) if (bl[i << 1] == bl[i << 1 | 1]) return ; flag = 1; for (i = 1 ; i <= n ; i ++ ) { if (str[i] == 'a' ) putchar (bl[i << 1] < bl[i << 1 | 1] ? 'B' : 'C' ); else if (str[i] == 'b' ) putchar (bl[i << 1] < bl[i << 1 | 1] ? 'A' : 'C' ); else putchar (bl[i << 1] < bl[i << 1 | 1] ? 'A' : 'B' ); } puts ( "" ); } void dfs( int x) { if (flag) return ; if (x > n) { solve(); return ; } if (str[x] == 'x' ) str[x] = 'a' , dfs(x + 1) , str[x] = 'b' ; dfs(x + 1); } int main() { int i; scanf ( "%d%*d%s%d" , &n , str + 1 , &m); for (i = 1 ; i <= m ; i ++ ) scanf ( "%d%s%d%s" , &px[i] , sx , &py[i] , sy) , vx[i] = sx[0] - 'A' , vy[i] = sy[0] - 'A' ; dfs(1); if (!flag) puts ( "-1" ); return 0; } |
分类:
题库 - uoj
, 图论 - 2-SAT
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· 趁着过年的时候手搓了一个低代码框架
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· 乌龟冬眠箱湿度监控系统和AI辅助建议功能的实现