题目大概意思为有 N 对锁(即有 2N 个锁),每对锁只能用其中一种,用了一种后,另一种会消失,有M 个大门,每个门有两个锁,开了其中一个就能打开门,问能从第一个门开始,一个个按顺序打开,最多打开多少门

二选一,有限制条件(每个限制条件中变量个数不超过 2 ),考虑 2-SAT
求的是最大可行解,考虑 二分

第一种:
我们考虑每个门之间的关系,例如有一对钥匙为 1 2 ,而门1 为 0 1,门2 为 0 2,这样如果选了 门1 的 锁1,则 钥匙2 消失,所以我们就必须选择 门2 的 锁0
(我们如果这样考虑每个门之间的关系,预处理连接各个边,这样的代码复杂度为 O( M ^ 2 ),能AC,毕竟 M 才2000多一点,但不是优解)
代码如下:( 820K 110MS )

#include <iostream>
#include <stdio.h>
#include <vector>
#include <string.h>
using namespace std;
vector<int> edge[6005];
vector<int> redge[6005];
vector<int> flag;
int used[6005];
int kind[6005];
int pipei[4005];
int data[3005][2];
int N, M;
void add_edge(int i, int j)
{edge[i].push_back(j);redge[j].push_back(i);
}
void dfs(int v, int n)
{used[v] = true;for(int i = 0; i < edge[v].size(); i++){if(!used[edge[v][i]] && edge[v][i] % M < n){dfs(edge[v][i], n);}}flag.push_back(v);
}
void rdfs(int v, int k, int n)
{used[v] = true;kind[v] = k;for(int i = 0; i < redge[v].size(); i++){if(!used[redge[v][i]] && redge[v][i] % M < n){rdfs(redge[v][i], k, n);}}
}
bool pan(int n)
{flag.clear();memset(used, false, sizeof(used));for(int i = 0; i < n; i++){if(!used[i]) dfs(i, n);}for(int i = M; i < M + n; i++){if(!used[i]) dfs(i, n);}memset(used, false, sizeof(used));int num = 0;for(int i = flag.size() - 1; i >= 0; i--){if(!used[flag[i]]) rdfs(flag[i], num++, n);}int res = 0;for(int i = 0; i < n; i++){if(kind[i] == kind[i + M]){return false;}}return true;
}
int main()
{while(1){scanf("%d %d", &N, &M);if(N == 0 && M == 0){break;}for(int i = 0; i < 2 * M; i++){edge[i].clear();redge[i].clear();}for(int i = 0; i < N; i++){int a, b;scanf("%d %d", &a, &b);pipei[a] = b;pipei[b] = a;}for(int i = 0; i < M; i++){scanf("%d %d", &data[i][0], &data[i][1]);}for(int i = 0; i < M; i++){for(int j = i + 1; j < M; j++){if(data[i][0] == pipei[data[j][0]]){add_edge(i, j + M);add_edge(j, i + M);}if(data[i][0] == pipei[data[j][1]]){add_edge(i, j);add_edge(j + M, i + M);}if(data[i][1] == pipei[data[j][0]]){add_edge(i + M, j + M);add_edge(j, i);}if(data[i][1] == pipei[data[j][1]]){add_edge(i + M, j);add_edge(j + M, i);}}}int l = 1, r = M + 1, mid = (l + r) / 2;while(l < r - 1){if(pan(mid)){l = mid;mid = (l + r) / 2;}else{r = mid;mid = (l + r) / 2;}}printf("%d\n", l);}
}

第二种:
我们考虑门自身,和钥匙自身的关系。我们每个门的两个锁,必须选其中一个,我们一对钥匙,最多选其中一个,即对于钥匙 ¬ i ∨ ¬ j ,对于门 i ∨ j 。 例如一对钥匙为 1 2 ,门1 为 0 1,则钥匙选了 1 ,就不能选 2,选了 2, 就不能选 1,门 1 没有选 0,则必须选 1,没有选 1,则必须选 0
(这种每次构建边最多只用 O( n ),乘上二分的 O( logn ),代码的复杂度为 O( n logn ),代码当然也可以AC)
代码如下:( 892K 63MS )

#include <iostream>
#include <stdio.h>
#include <vector>
#include <string.h>
using namespace std;
vector<int> edge[10005];
vector<int> redge[10005];
vector<int> flag;
int used[10005];
int kind[10005];
int pipei[10005];
int data[3005][2];
int N, M;
void add_edge(int i, int j)
{edge[i].push_back(j);redge[j].push_back(i);
}
void dfs(int v)
{used[v] = true;for(int i = 0; i < edge[v].size(); i++){if(!used[edge[v][i]]){dfs(edge[v][i]);}}flag.push_back(v);
}
void rdfs(int v, int k)
{used[v] = true;kind[v] = k;for(int i = 0; i < redge[v].size(); i++){if(!used[redge[v][i]]){rdfs(redge[v][i], k);}}
}
bool pan(int n)
{flag.clear();for(int i = 0; i < 4 * N; i++){edge[i].clear();redge[i].clear();}for(int i = 0; i < 2 * N; i++){add_edge(i, pipei[i] + 2 * N);}for(int i = 0; i < n; i++){add_edge(data[i][0] + 2 * N, data[i][1]);add_edge(data[i][1] + 2 * N, data[i][0]);}memset(used, false, sizeof(used));for(int i = 0; i < 2 * 2 * N; i++){if(!used[i]) dfs(i);}memset(used, false, sizeof(used));int num = 0;for(int i = flag.size() - 1; i >= 0; i--){if(!used[flag[i]]) rdfs(flag[i], num++);}for(int i = 0; i < 2 * N; i++){if(kind[i] == kind[i + 2 * N]){return false;}}return true;
}
int main()
{while(1){scanf("%d %d", &N, &M);if(N == 0 && M == 0){break;}for(int i = 0; i < N; i++){int a, b;scanf("%d %d", &a, &b);pipei[a] = b;pipei[b] = a;}for(int i = 0; i < M; i++){scanf("%d %d", &data[i][0], &data[i][1]);}int l = 0, r = M + 1, mid = (l + r) / 2;while(l < r - 1){if(pan(mid)){l = mid;mid = (l + r) / 2;}else{r = mid;mid = (l + r) / 2;}}printf("%d\n", l);}
}

(末尾吐槽,这道题我pipei数组开小了一点点,告诉我超时,搞得我找了一天哪里复杂度高了,无语子!!!!!)

poj2723详解(二分 + 2-SAT)( 两种方法求解 )相关推荐

  1. 百钱买百鸡python编程列表推导式_使用循环和列表推导式两种方法求解百钱买百鸡问题。假设大鸡5元一只,中鸡3元一只,小鸡1元三只,现有100元钱想买100只鸡,有多少种买法?...

    [程序题]编写一个函数,输入n为偶数时,调用函数求1/2+1/4+...+1/n,当输入n为奇数时,调用函数 1/1+1/3+...+1/n [单选题]患者男,67岁.确诊为原发性支气管肺癌,为行手术 ...

  2. 图解法求最优解的例题_初一上学期,方程的解互为相反数,两种方法求解参数的值...

    在一元一次方程问题中,有一类问题,那就是方程的解互为相反数.在处理这类问题时,一般有两种方法进行处理,不同的题目可以选择不同的方法.当然,两种方法都需要掌握. 例题1:已知关于x的方程6x-a=1+4 ...

  3. Python两种方法求解登楼梯问题(京东2016笔试题)

    问题:假设一段楼梯共15个台阶,小明一步最多能上3个台阶,那么小明上这段楼梯一共有多少种方法? 解析:从第15个台阶上往回看,有3种方法可以上来(从第14个台阶上一步迈1个台阶上来,从第13个台阶上一 ...

  4. 详解SpringCloud-gateway动态路由两种方式,以及路由加载过程

    gateway配置路由主要有两种方式,一种是用yml配置文件,一种是写代码里,这两种方式都是不支持动态配置的.如: 下面就来看看gateway是如何加载这些配置信息的. 1 路由初始化 无论是yml还 ...

  5. 0-1背包问题详解(DP分支限界回溯三种方法)

    0-1背包 将n个项目的权重和值,放入一个容量为W的背包中,得到背包中最大的总价值.换句话说,给定两个整数数组val[0..n - 1]和wt [0 . .n-1],分别表示与n个项目相关的值和权重. ...

  6. 两种方法求解 正数数组中 两个数相减 的最大值

    一,问题描述 给定一个正数数组arr(即数组元素全是正数),找出该数组中,两个元素相减的最大值,其中被减数的下标不小于减数的下标. 即求出: maxValue = max{arr[j]-arr[i] ...

  7. ROS中Remap标签详解,举例说明其两种用法

    1.< remap>标签: < remap>标签"允许你以更结构化的方式将名称重新映射参数传递给ROS节点,而不是直接设置<节点>的参数属性. 2.作用: ...

  8. 用两种方法求解九宫算问题

    问题: 在一个3*3的方格内填入9个数字,数字的范围从1~9,每个数字仅出现一次,使得每行.每列以及两个对角线的数字之和为15.  一.VBA程序 算法思路: 1.将9个数字分成3组,每组3个数字,使 ...

  9. java登录代码带验证码实现_两种方法实现带验证码的用户登录

    带验证码的登录效果图 登录成功时,记录用户名和访问次数 登录失败,弹出错误提示 方法一:应用request对象获取表单数据实现登录操作 代码结构: index.jsp源码及详解: 当表单被提交时,数据 ...

最新文章

  1. 用MySQL创建“江湖”数据库
  2. 阅读源代码的一点小技巧
  3. 最新的20多个JMS面试问答(2020)
  4. cad2016珊瑚_预测有马的硬珊瑚覆盖率
  5. 如何激活Microsoft Office 2010?
  6. 雅虎JavaScript架构师:网页开发技术安全优先
  7. Rust: mod文件、main文件调用
  8. 【总线】一文看懂 I2C 通信协议
  9. After Effects - Bodymovin 插件验货及感受
  10. 移远NB-IOT模块BC26资料
  11. Linux权限与sudo
  12. Java高性能的编程技巧
  13. 云数据库RDS规格族配置详解
  14. linux ksoftirqd进程,ksoftirqd进程导致cpu消耗殆尽
  15. asp.net 大型商家联盟会员积分消费管理系统,带微信和文档【源码分享】
  16. 有强烈的危机感,是一种病吗?
  17. 回家过年要花多少钱?算完感觉生无可恋~
  18. 移动端UI设计规范模板参考以及设计规范的好处
  19. eclipse oxygen配置tomcat
  20. 2020-10-08 吴恩达-C4 卷积神经网络-w2 CNN实例探究(课后编程1-Keras tutorial - the Happy House-笑脸识别)

热门文章

  1. Python3.5+SQL+Prometheus+Grafana报表/监控
  2. 指针05 - 零基础入门学习C语言45
  3. 一个 零差评的 Python 内置库
  4. html 图片时钟,教你五步制作精美的HTML时钟
  5. android点击事件注册方式,Android界面控件(3)— 注册同一个点击事件的监听器...
  6. java怎样控制迭代周期_如何在Java 8中一年中每周迭代一次?
  7. 简单介绍实体类或对象序列化时,忽略为空属性的操作
  8. 树莓派 Zero 与 Zero W 对比
  9. STM32单片机真的落后?
  10. apache php php.ini,apache php环境搭建 httpd.conf php.ini 修改点