2-sat总结

2-sat问题总的来说就是把n个物品分成两个集合,或者说2*n个物品有n对,每对只能挑一个。实际上就是建模成,每一对选一个0表示第一个,1表示第二个(或一个物品选(1)与不选(0)),也就是用0,1来表示互相矛盾的物品,然后物品之间相互有一些限制关系:对于一对我们用 i0 i1 表示。两个物品分别为i,j。

1.与
1:两个都为1.也就是不能选0:那么建边 i0 i1 j0 j1 。(使其自相矛盾即可)
0:两个至少有一个为0:那么建边 i1 j0 j1 i0
2.或
1:至少有一个为1:那么建边 i0 j1 j0 i1
0:都为0,也就是不能选1:那么建边 i1 i0 j1 j0
3.异或
1:两者不同:建边 i0 j1 j1 i0
0:两者相同:建边 i0 j0 i1 j1
最后都可以归为这6种关系。然后建图跑强连通就可以了。
1.n个物品,选与不选的问题。如果i与j冲突,则建边:

head[i<<1].push_back(j<<1|1);
head[j<<1|1].push_back(i<<1);
head[j<<1].push_back(i<<1|1);
head[i<<1|1].push_back(j<<1);

2.2*n个物品,只能选一个的问题。如果有冲突,则建边:

if(check(i<<1,j<<1))head[i<<1].push_back(j<<1|1);
if(check(i<<1,j<<1|1))head[i<<1].push_back(j<<1);
if(check(i<<1|1,j<<1))head[i<<1|1].push_back(j<<1|1);
if(check(i<<1|1,j<<1|1))head[i<<1|1].push_back(j<<1);

输出解:拓扑序出来的一组就是解。

for(int i = 0; i < n; ++i)
        {
            ha[sccno[i<<1]].push_back(sccno[i<<1|1]);
            ha[sccno[i<<1|1]].push_back(sccno[i<<1]);
        }
        for(int i = 0; i < n*2; ++i)
            for(int j = 0,l = head[i].size(); j < l; ++j)
            {
                int u = sccno[i];
                int v = sccno[head[i][j]];
                if(u != v)
                {
                    G[v].push_back(u);
                    du[u]++;
                }
            }
        get_ans();
int color[MAXN];
int du[MAXN];
void get_ans()
{
    queue<int>q;
    for(int i = 1; i <= scc_cnt; ++i)if(!du[i])q.push(i);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        if(!color[u])
        {
            color[u] = 1;
            for(int i = 0, l = ha[u].size(); i < l; ++i)
            {
                int v = ha[u][i];
                color[v] = 2;
            }
        }
        for(int i = 0, l = G[u].size(); i < l; ++i)
        {
            int v = G[u][i];
            du[v]--;
            if(!du[v])q.push(v);
        }
    }
    puts("YES");
    for(int i = 0; i < n; ++i)
    {
        if(color[sccno[i<<1]] == 1)
            printf("%.2d:%.2d %.2d:%.2d\n",w[i<<1].s/60,w[i<<1].s%60,w[i<<1].e/60,w[i<<1].e%60);
        else printf("%.2d:%.2d %.2d:%.2d\n",w[i<<1|1].s/60,w[i<<1|1].s%60,w[i<<1|1].e/60,w[i<<1|1].e%60);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值