HDU - 3062 Party 2-SAT问题 tarjan 模板

本文详细介绍了2-SAT问题及其解决方法,通过构造有向图并利用Tarjan算法来判断是否存在矛盾,最终实现从每对夫妻中选出一人参加聚会的问题求解。

题意:中文题,有n对夫妻被邀请参加一个聚会,因为场地的问题,每对夫妻中只有1人可以列席。在2n 个人中,某些人之间有着很大的矛盾(当然夫妻之间是没有矛盾的),有矛盾的2个人是不会同时出现在聚会上的。有没有可能会有n 个人同时列席?

SAT是适定性(Satisfiability)问题的简称 。一般形式为k-适定性问题,简称 k-SAT。 
当k>2时,k-SAT是NP完全的。因此一般讨论的是k=2的情况,即2-SAT问题。 
2-SAT,简单的说就是给出n个集合,每个集合有两个元素, 
已知若干个 < a,b >,表示a与b矛盾(其中a与b属于不同的集合)。 
然后从每个集合选择一个元素,一共选n个两两不矛盾的元素。 
显然可能有多种选择方案,一般题中只需要求出一种即可。

2-SAT问题

构造一个有向图G,每个变量xi拆成两个点2i和2i+1 
分别表示xi为假,xi为真 
那么对于“xi为真或xj为假”这样的条件 
我们就需要连接两条边 
2*i —>2*j(表示如果i为假,那么j必须是假) (重点)
2*j+1—>2*i+1(表示如果j为真,那么i必须是真) 
这就有点像推导的过程 
实际上每一个限制条件都会对应两条“对称”的边

接下来逐考虑每个没有赋值的变量,设为x 
我们先假定x为假(为什么一定是假,约定俗成了) 
之后沿着从ta出发的有向边进行标记 
如果在标记过程中,发现有一个点的两种状态都被标记过了 
那么我们之前的假设就被推翻了 
需要改成x为真,重新标记 
如果发现无论这个点赋值成真还是假,都会引起矛盾 
可以证明这个2-SAT无解

这个问题就是个模板题,加一下边就好了

链接:hdu - 3062

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>
#define INF 0x3f3f3f3f

using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 100005;
const int maxm = 4000005;
//a<<1 和 (a<<1) + 1。a<<1表示妻子,(a<<1) + 1表示丈夫
//连接某边是为了推出矛盾。x->y表示选x则必须选y
//注意方向
struct node {
    int s, t;
    int nxt;
} e[maxm];

int n, m;
int idx, ans, tp, cont;
int dfn[maxn], vis[maxn], low[maxn], head[maxn], st[maxn], belong[maxn];

void init() {
    cont = 0;
    memset(head, -1, sizeof(head));
    memset(vis, 0, sizeof(vis));
    memset(dfn, 0, sizeof(dfn));
    idx = tp = ans = 0;
}
 
void add(int s, int t) {
    e[cont].s = s;
    e[cont].t = t;
    e[cont].nxt = head[s];
    head[s] = cont++;
}

void tarjan(int u) {
    dfn[u] = low[u] = ++idx;
    vis[u] = 1;
    st[++tp] = u;
    int v ;
    for(int i = head[u]; i != -1; i = e[i].nxt) {
        v = e[i].t ;
        if(!dfn[v]) {
            tarjan(v) ;
            low[u] = min(low[u],low[v]);
        }
        else if(vis[v])
            low[u] = min(low[u],dfn[v]);
    }
    if(dfn[u] == low[u]) {
        ans++;
        while(1) {
            v = st[tp--];
            vis[v] = 0;
            belong[v] = ans;
            if(v == u)
                break;
        }
    }
}

bool _2sat() {
    for(int i = 0; i < 2 * n; i++)
        if(!dfn[i])
            tarjan(i) ;
    for(int i = 0; i < n; i++)
        if(belong[2 * i] == belong[(2 * i) ^ 1])//矛盾
            return false;
    return true;
}
 
int main()
{
    int a, b, c, d;
    while(~scanf("%d %d",&n, &m)) { 
        init();
        for(int i = 0; i < m; i++) {
            scanf("%d %d %d %d", &a, &b, &c, &d);
            //int u, v;
            //u = a*2+c; v = b*2+d;
            //add(u, v^1); add(v, u^1);
            if(c == 0 && d == 0) {//两个妻子
                add(a << 1, (b << 1) + 1);
                add(b << 1, (a << 1) + 1);
            }
            else if(c == 0 && d == 1) {//妻子和丈夫 
                add((a << 1), (b << 1));
                add((b << 1) + 1, (a << 1) + 1);
            }
            else if(c == 1 && d == 0) {//丈夫和妻子 
                add((a << 1) + 1, (b << 1) + 1);
                add(b << 1, a << 1);
            }
            else if(c == 1 && d == 1) {//两个丈夫有矛盾 
                add((a << 1) + 1, b << 1);
                add((b << 1) + 1, a << 1);
            }
        }
        if(_2sat())
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0 ;
}

 

内容概要:本文详细介绍了“秒杀商城”微服务架构的设计与实战全过程,涵盖系统从需求分析、服务拆分、技术选型到核心功能开发、分布式事务处理、容器化部署及监控链路追踪的完整流程。重点解决了高并发场景下的超卖问题,采用Redis预减库存、消息队列削峰、数据库乐观锁等手段保障数据一致性,并通过Nacos实现服务注册发现与配置管理,利用Seata处理跨服务分布式事务,结合RabbitMQ实现异步下单,提升系统吞吐能力。同时,项目支持Docker Compose快速部署和Kubernetes生产级编排,集成Sleuth+Zipkin链路追踪与Prometheus+Grafana监控体系,构建可观测性强的微服务系统。; 适合人群:具备Java基础和Spring Boot开发经验,熟悉微服务基本概念的中高级研发人员,尤其是希望深入理解高并发系统设计、分布式事务、服务治理等核心技术的开发者;适合工作2-5年、有志于转型微服务或提升架构能力的工程师; 使用场景及目标:①学习如何基于Spring Cloud Alibaba构建完整的微服务项目;②掌握秒杀场景下高并发、超卖控制、异步化、削峰填谷等关键技术方案;③实践分布式事务(Seata)、服务熔断降级、链路追踪、统一配置中心等企业级中间件的应用;④完成从本地开发到容器化部署的全流程落地; 阅读建议:建议按照文档提供的七个阶段循序渐进地动手实践,重点关注秒杀流程设计、服务间通信机制、分布式事务实现和系统性能优化部分,结合代码调试与监控工具深入理解各组件协作原理,真正掌握高并发微服务系统的构建能力。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值