zoukankan      html  css  js  c++  java
  • 【uoj#317】[NOI2017]游戏 2-SAT

    题目描述

    给出 $n$ 个赛车赛道和A、B、C三种赛车,除了 $d$ 个赛道可以使用所有三种赛车以外每个都只能使用给出的两种之一。另外给出 $m$ 条限制:某个赛道使用X则某另一个赛道必须使用Y。问:是否存在一种方案满足所有条件?输出一种合法方案。

    $nle 50000,dle 8,mle 100000$ 。


    题解

    2-SAT

    3-SAT是NP完全问题,由于 $d$ 只有 $8$ ,因此考虑枚举每个万能位置的取值,转化为2-SAT问题。

    那么对于一条限制,显然描述对应着一条边;另外一个命题的逆否命题,因此则有:第二个不用Y,第一个就不能用X,还要连这样的边(考场上没有想到对称边,还以为标算不是2-SAT)。

    特殊情况:
    第一个没有X,则无视这条边;
    第二个没有Y,则第一个不能选X,第一个选X向不选X连边。

    然后跑tarjan,对立点在同一强连通分量里则不成立,否则有解。缩点建新图跑拓扑排序,对立点中先排到的点不选,后排到的选。

    这里有一个小trick:tarjan中强连通分量的编号顺序就是逆拓扑序(考虑tarjan的过程,挺好理解的),因此不用实际拓扑排序,直接比较对立点所属强连通分量编号即可,较小的那个是相应方案。

    这样枚举万能位置的选择,每个位置有三种情况。考虑到枚举万能位置不能选什么,一次可以选出两种,只需要枚举两种情况。

    时间复杂度 $O(2^dm)$

    #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;
    }
    
  • 相关阅读:
    JAVA 读取Properties文件内容乱码 解决方法
    java代码求IP和mac地址
    jsp 清除session的方法
    Go语言字符串和正则表达式
    go语言:字符串操作
    博客园美化基本完成!!!
    美化了一下页面
    第一天:初入博客园
    c语言实参与形参的区别
    c链表中指针的一些用法要点
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8379708.html
Copyright © 2011-2022 走看看