2-SAT 问题

推荐blog:

https://www.cnblogs.com/cjoieryl/p/8460181.html

https://www.cnblogs.com/-ZZB-/p/6635483.html

https://blog.youkuaiyun.com/jarjingx/article/details/8521690

 

2-SAT的定义:

  当前有一些集合,每个集合中有且仅有2个元素,且不能同时被选取,集合与集合间的元素存在矛盾关系,求可行解及其方案。

 

求解2-SAT的过程:

  ①根据题意判断模型并建边。

  ②:Tarjan求强连通分量。

  ③:判断可行性。

 

模型:

  ①:(A,B)不能同时取 A->B' A'->B

    选了A就只能选B',选了A'就只能选B

  ②:(A,B)不能同时不取  A'->B  B'->A

    选了A'必定选B,选了B'必定选A

  ③:(A,B)要么都取,要么都不取 A->B  B->A  A'->B' B'->A'

    选了A只能选B,选了B只能选A,选了A'只能选B',选了B'只能选A'

 

做Tarjan的原因:

  在连边的时候点与点之间是单向边,那么我们连着连着就可能出现强连通分量,而出现强连通分量就说明了这个强连通分量中只要选了一个点其他的点就必须全部被选,这样就可以判断有冲突的两个元素是否同时必须被选了。Tarjan是用来求强连通分量的。

 

输出方案:

  由于Tarjan是基于DFS实现的,2-SAT具有对称性,那么假如这一种方案可行,那么另一种方案也可行,并且这两种方案中的点的所属的强连通分量的编号也是有大小关系的。对于任意两个有冲突的元素中,所属编号大的是一种方案,编号小的又是另一种方案。

 

习题:

  Luogu 4782 【模板】2-SAT 问题:https://www.luogu.org/problemnew/show/P4782

   解题思路:

        模板题。。。

  

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
#define INF 0x3f3f3f3f
#define maxn 2000009
#define maxm
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ll)(ch-'0');ch=getchar();}
    return x*f;
}
stack<int>s;
int head[maxn],dfn[maxn],low[maxn],belong[maxn];
int n,m,k,ans,tot,cnt,id,block;
struct edge
{
    int to,nxt;
}p[maxn<<2];
void add(int x,int y)
{
    p[++cnt]={y,head[x]},head[x]=cnt;
} 
 
void Tarjan(int u)
{
    dfn[u]=low[u]=++id,s.push(u);
    for(int i=head[u];i;i=p[i].nxt)
    {
        int v=p[i].to;
        if(!dfn[v])
        {
            Tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!belong[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])
    {
        ++block;
        while(1)
        {
            int v=s.top();s.pop();
            belong[v]=block;
            if(u==v)
                break;
        }
    }
}
bool TWO_SAT()
{
    for(int i=1;i<=2*n;i++)
        if(!dfn[i])
            Tarjan(i);
    for(int i=1;i<=n;i++)
        if(belong[i]==belong[i+n])
            return false;
    return true;
}
int main()
{
//    freopen(".in","r",stdin);
//    freopen(".out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),fx=read(),y=read(),fy=read();
        int gx=!fx,gy=!fy;
        add(x+gx*n,y+fy*n);
        add(y+gy*n,x+fx*n);
    }
    if(TWO_SAT())
    {
        puts("POSSIBLE");
        for(int i=1;i<=n;i++)
            if(belong[i]<belong[i+n])
                printf("0 ");
            else
                printf("1 ");
    }
    else
        puts("IMPOSSIBLE");
    fclose(stdin);
    fclose(stdout);
    return 0;
}
View Code

 

 

  Loj #10097. 「一本通 3.5 练习 5」和平委员会:https://loj.ac/problem/10097

  解题思路:还是模板题。。

  

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
#define INF 0x3f3f3f3f
#define maxn 16009
#define maxm 20009
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ll)(ch-'0');ch=getchar();}
    return x*f;
}
stack<int>s;
int head[maxn],dfn[maxn],low[maxn],belong[maxn];
struct edge
{
    int to,nxt;
}p[maxm<<1];
int n,m,k,ans,tot,cnt,id,block; 

void add(int x,int y)
{
    p[++cnt]={y,head[x]},head[x]=cnt;
}


void Tarjan(int u)
{
    dfn[u]=low[u]=++id,s.push(u);
    for(int i=head[u];i;i=p[i].nxt)
    {
        int v=p[i].to;
        if(!dfn[v])
        {
            Tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!belong[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])
    {
        ++block;
        while(1)
        {
            int v=s.top();s.pop();
            belong[v]=block;
            if(u==v)
                break;
        }
    }
}
bool TWO_SAT()
{
    for(int i=1;i<=n*2;i++)
        if(!dfn[i])
            Tarjan(i);
    for(int i=1;i<=n*2;i+=2)
        if(belong[i]==belong[i+1])
            return false;
    return true;
}
priority_queue<int>q;
int main()
{
//    freopen(".in","r",stdin);
//    freopen(".out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        int fx=(x&1)?1:0,fy=(y&1)?1:0;
        add(x,fy==1?y+1:y-1);
        add(y,fx==1?x+1:x-1);
    }
    if(TWO_SAT())
    {
        for(int i=1;i<=2*n;i+=2)
             if(belong[i]<belong[i+1])
                 q.push(-i);
            else
                q.push(-(i+1));
        while(q.size())
        {
            printf("%d\n",-q.top());
            q.pop(); 
        }
    } 
    else
        puts("NIE");
    fclose(stdin);
    fclose(stdout);
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/Dxy0310/p/9809845.html

乐播投屏是一款简单好用、功能强大的专业投屏软件,支持手机投屏电视、手机投电脑、电脑投电视等多种投屏方式。 多端兼容与跨网投屏:支持手机、平板、电脑等多种设备之间的自由组合投屏,且无需连接 WiFi,通过跨屏技术打破网络限制,扫一扫即可投屏。 广泛的应用支持:支持 10000+APP 投屏,包括综合视频、网盘与浏览器、美韩剧、斗鱼、虎牙等直播平台,还能将央视、湖南卫视等各大卫视的直播内容一键投屏。 高清流畅投屏体验:腾讯独家智能音画调校技术,支持 4K 高清画质、240Hz 超高帧率,低延迟不卡顿,能为用户提供更高清、流畅的视觉享受。 会议办公功能强大:拥有全球唯一的 “超级投屏空间”,扫码即投,无需安装。支持多人共享投屏、远程协作批注,PPT、Excel、视频等文件都能流畅展示,还具备企业级安全加密,保障会议资料不泄露。 多人互动功能:支持多人投屏,邀请好友加入投屏互动,远程也可加入。同时具备一屏多显、语音互动功能,支持多人连麦,实时语音交流。 文件支持全面:支持 PPT、PDF、Word、Excel 等办公文件,以及视频、图片等多种类型文件的投屏,还支持网盘直投,无需下载和转格式。 特色功能丰富:投屏时可同步录制投屏画面,部分版本还支持通过触控屏或电视端外接鼠标反控电脑,以及在投屏过程中用画笔实时标注等功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值