Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

有N个变量,每个变量都能取T或F,需要满足M个条件: or

要给每个变量赋值,满足所有条件.

构造有向图G,每个拆成两个点2i和2i+1, 分别表示取T或者F. 每个变量选其中的一个进行标记,标记了节点2i表示,标记2i+1表示.

对于每个条件,如 or ,可以从表示点i为F的节点到表示点j为F的节点连一条边,表示如果点i为F,那么点j一定是F. 同时,还要从表示点j为T的点向表示点i为T的点连一条边.

DFS方法

逐个考虑每个未赋值的变量,先假定为真,标记结点2i,然后沿着边标记所有可以标记的点,并记录这个过程中标记了的点,方便更换这个变量的赋值.如果发现某个变量的两个点都被标记了,说明这个变量不可能为真,就要按照刚刚的记录,将标记去掉,清空记录,标记节点2i+1,重复过程.注意在假定为真的dfs之前也要清空记录. 如果这时候还不行,这个问题就无解,即使改变之前变量的值也没用.

luogu P4782 模板

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <algorithm>
#define MAXN 1000005
#define INF 0x3f3f3f3f
#define lovelive long long int
#define osu(a,b,i) for(int i = (a);i <= (b);i++)
#define nso(a,b,i) for(int i = (a);i >= (b);i--)
inline int homax(int a,int b)
{
    if(a > b) return a;
    return b;
}
inline int homin(int a,int b)
{
    if(a > b) return b;
    return a;
}
using namespace std;
bool vis[MAXN*2];
vector<int> sv;
vector<int> G[MAXN*2];
bool dfs(int x)
{
    if(vis[x^1]) return false;
    if(vis[x]) return true;
    vis[x] = true;
    sv.push_back(x);
    for(auto to : G[x])
    {
        if(!dfs(to)) return false;
    }
    return true;
}
void addcons(int x,int vx,int y,int vy)
{
    G[(x << 1 | vx)^1].push_back(y << 1 | vy);
    G[(y << 1 | vy)^1].push_back(x << 1 | vx);
    return ;
}
int N,M;
bool sat2()
{
    osu(1,N,i)
    {
        if(vis[i << 1] || vis[i << 1 | 1]) continue;
        sv.clear();//假定为真的dfs之前清空记录
        if(!dfs(i << 1))
        {
            while(!sv.empty()) vis[sv.back()] = false, sv.pop_back();
            if(!dfs(i << 1 | 1))
            {
                return false;
            }
        }
    }
    return true;
}
int main()
{
    scanf("%d%d",&N,&M);
    osu(1,M,i)
    {
        int a,b,c,d;
        scanf("%d%d%d%d",&a,&b,&c,&d);
        addcons(a,b,c,d);
    }
    if(sat2())
    {
        cout << "POSSIBLE" << endl;
        osu(1,N,i)
        {
            if(vis[i << 1])
            {
                cout << 0 << " ";
            }else{
                cout << 1 << " ";
            }
        }
    }else{
        cout << "IMPOSSIBLE" << endl;
    }
    return 0;
}

复杂度

这个方法还可以保证答案的字典序,对于大部分的题目已经够用了.

SCC方法

用Tarjan对每个SCC缩点,如果某个变量对应的两个节点在一个SCC中,则问题无解,否则一定有解.

如果一个变量的T结点所在SCC的拓扑序大于F结点的,那么就给这个变量赋T,否则赋F,用这个方法一定给所有有解的情况构造出答案.

复杂度,达到了输入下限.但由于无法保证解的字典序,一般只用于判断是否有解.

例题

Now or later, UVA-1146

https://vjudge.net/problem/UVA-1146

题目大意

每个飞机都有2个降落时间,早降落时间和晚降落时间,每个飞机都可以选择早或者晚降落,每个飞机的早晚降落时间是固定的,但是可以和其他飞机不同.

你可以任意指定每个飞机的早晚降落情况,使每个飞机真正的降落时间之间的间隔最大.

解法

二分答案T,用代表第x个飞机的早降落时间和晚降落时间,如果, 说明不能同时选,即, 由狄摩根定律,这个条件等价于 | ,这就转化为了2-SAT判断有解性问题.

https://vjudge.net/solution/24649633

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <algorithm>
#define MAXN 2005
#define INF 0x3f3f3f3f
#define lovelive long long int
#define osu(a,b,i) for(int i = (a);i <= (b);i++)
#define nso(a,b,i) for(int i = (a);i >= (b);i--)
inline int homax(int a,int b)
{
    if(a > b) return a;
    return b;
}
inline int homin(int a,int b)
{
    if(a > b) return b;
    return a;
}
inline int hoabs(int a)
{
    if(a > 0) return a;
    return -a;
}
using namespace std;
struct twosat{
    int N;
    bool vis[MAXN*2];
    vector<int> sv;
    vector<int> G[MAXN*2];
    bool dfs(int x)
    {
        if(vis[x^1]) return false;
        if(vis[x]) return true;
        vis[x] = true;
        sv.push_back(x);
        for(auto to : G[x])
        {
            if(!dfs(to)) return false;
        }
        return true;
    }
    void init()
    {
        memset(vis,false,sizeof vis);
        sv.clear();
        for(auto &v : G) v.clear();
        return ;
    }
    void addcons(int x,int vx,int y,int vy)
    {
        G[(x << 1 | vx)^1].push_back(y << 1 | vy);
        G[(y << 1 | vy)^1].push_back(x << 1 | vx);
        return ;
    }
    bool sat2()
    {
        osu(1,N,i)
        {
            if(vis[i << 1] || vis[i << 1 | 1]) continue;
            sv.clear();
            if(!dfs(i << 1))
            {
                while(!sv.empty()) vis[sv.back()] = false, sv.pop_back();
                if(!dfs(i << 1 | 1))
                {
                    return false;
                }
            }
        }
        return true;
    }
}ts;
int N;
int tb[MAXN][2];
bool judge(int x)
{
    ts.init();
    ts.N = N;
    osu(1,N,i) osu(i+1,N,j)
    {
        osu(0,1,k) osu(0,1,l)
        {
            if(hoabs(tb[i][k] - tb[j][l]) < x)
            {
                ts.addcons(i,k^1,j,l^1);
            }
        }
    }
    return ts.sat2();
}
int solve()
{
    int l = 0,r = 2e7;
    while(l < r)
    {
        int mid = ((l + r + 1) >> 1);
        if(judge(mid))
        {
            l = mid;
        }else{
            r = mid-1;
        }
    }
    return l;
}

int main()
{
    
    while(~scanf("%d",&N))
    {
        memset(tb,0,sizeof tb);
        osu(1,N,i)
        {
            osu(0,1,j)
            {
                scanf("%d",&tb[i][j]);
            }
        }
        cout << solve() << endl;
    }
}

(明明UVALive-3211和UVA-1146是一样的题,但是在vjudge上一样的代码只能过UVA-1146...刚开始交UVALive-3211怎么都过不去,换了个一模一样的题交就过了,魔幻)

评论