poj3683
poj3683
题意
n对新人举行婚礼,婚礼在不同时间段但可能重叠,婚礼有开始(Si)、结束(Ti)、仪式举行时间(Di),问能否给出一种举行方案,使得神父能参加所有的婚礼并举行仪式。
分析
xi为真 <=> 在开始时举行仪式,
xj为假 <=> 在结束时举行仪式。
设x' 为 非x。(若 x 为真,则 x' 为假)
xi -> xj 表示 xi 为真则 xj 为真。(xi xj 连边)
对于结婚仪式 i 和 j ,如果 Si ~ Si+Di 和 Sj ~ Sj+Dj 冲突,就有 xi' or xj' ,这种情况即 xi -> xj' ,xj -> xi' 。(xi 和 xj 不能同时选,即选择一个,那么选了 xi 后 就会选择到 xj' ,表示不选 xj。)
如果 Si ~ Si+Di 和 Tj-Dj ~ Dj 冲突,有 xi' or xj ,即 xi -> xj,xj' -> xi' 。
如果 Ti-Di ~ Ti 和 Sj ~ Sj+Dj 冲突,有 xi or xj' ,即 xi' -> xj',xj -> xi 。
如果 Ti-Di ~ Ti 和 Tj-Dj ~ Dj 冲突 ,有 xi or xj ,即 xi' -> xj ,xj' -> xi 。
代码参照《 挑战程序设计竞赛》,网上的代码大多是按照论文进行模拟?
书中有个步骤比较疑惑,打印可行解时,
x 所在的强连通分量的拓扑序在 x' 所在的强连通分量之后 <=> x 为真。
一点理解:拓扑序靠后的出度小,越不会影响后面的选择?
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
typedef long long ll;
using namespace std;
const int MAXN = 2e3 + 10;
int n, m; // 点、边
int vis[MAXN];
int flag[MAXN]; // 所属强连通分量的拓扑序
vector<int> G[MAXN], rG[MAXN];
vector<int> vs; // 后序遍历顺序的顶点列表
void addedge(int x, int y)
{
G[x].push_back(y); // 正向图
rG[y].push_back(x); // 反向图
}
void dfs(int u)
{
vis[u] = 1;
for(int i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
if(!vis[v]) dfs(v);
}
vs.push_back(u);
}
void rdfs(int u, int k)
{
vis[u] = 1;
flag[u] = k;
for(int i = 0; i < rG[u].size(); i++)
{
int v = rG[u][i];
if(!vis[v]) rdfs(v, k);
}
}
int scc() // 强连通分量的个数
{
vs.clear();
memset(vis, 0, sizeof vis);
for(int i = 1; i <= n; i++) // [1...n]
if(!vis[i]) dfs(i);
memset(vis, 0, sizeof vis);
int k = 0;
for(int i = vs.size() - 1; i >= 0; i--)
if(!vis[vs[i]]) rdfs(vs[i], k++);
return k;
}
int S[MAXN], D[MAXN], T[MAXN];
void solve()
{
int N = n;
n = 2 * n;
for(int i = 1; i <= n; i++)
{
G[i].clear();
rG[i].clear();
}
for(int i = 1; i <= n; i++) // 枚举、连边
{
for(int j = 1; j < i; j++)
{
if(min(S[i] + D[i], S[j] + D[j]) > max(S[i], S[j]))
{
// xi -> xj' ,xj -> xi'
addedge(i, j + N);
addedge(j, i + N);
}
if(min(S[i] + D[i], T[j]) > max(S[i], T[j] - D[j]))
{
// xi -> xj,xj' -> xi'
addedge(i, j);
addedge(j + N, i + N);
}
if(min(T[i], S[j] + D[j]) > max(S[j], T[i] - D[i]))
{
// xi' -> xj',xj -> xi
addedge(i + N, j + N);
addedge(j, i);
}
if(min(T[i], T[j]) > max(T[i] - D[i], T[j] - D[j]))
{
// xi' -> xj ,xj' -> xi
addedge(i + N, j);
addedge(j + N, i);
}
}
}
scc();
// 判断可行性
for(int i = 1; i <= N; i++)
{
if(flag[i] == flag[N + i])
{
puts("NO");
return;
}
}
// 输出可行解
puts("YES");
for(int i = 1; i <= N; i++)
{
if(flag[i] > flag[i + N]) // 在仪式开始时举行
printf("%02d:%02d %02d:%02d\n", S[i] / 60, S[i] % 60, (S[i] + D[i]) / 60, (S[i] + D[i]) % 60);
else
printf("%02d:%02d %02d:%02d\n", (T[i] - D[i]) / 60, (T[i] - D[i]) % 60, T[i] / 60, T[i] % 60);
}
}
int main()
{
while(~scanf("%d", &n))
{
for(int i = 1; i <= n; i++)
{
int x1, y1, x2, y2;
char p, q;
scanf("%02d:%02d %02d:%02d %d", &x1, &y1, &x2, &y2, &D[i]);
S[i] = x1 * 60 + y1;
T[i] = x2 * 60 + y2;
}
solve();
}
return 0;
}
poj3683的更多相关文章
- 2-sat——输出方案poj3683
一篇讲的详细的博客 https://blog.csdn.net/Hawo11/article/details/74908233 缩点后为什么要建立反图? 如果是按原图处理,选择一个点之后要把所有其后续 ...
- 2-sat 输出任意一组可行解&拓扑排序+缩点 poj3683
Priest John's Busiest Day Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 8170 Accept ...
- poj3683 Priest John's Busiest Day
2-SAT 输出可行解 找可行解的方案就是: 根据第一次建的图建一个反图..然后求逆拓扑排序,建反图的原因是保持冲突的两个事件肯定会被染成不同的颜色 求逆拓扑排序的原因也是为了对图染的色不会发生冲突, ...
- poj3683 Priest John's Busiest Day
2-SAT. 读入用了黄学长的快速读入,在此膜拜感谢. 把每对时间当作俩个点.如果有交叉代表相互矛盾. 然后tarjan缩点,这样就能得出当前的2-SAT问题是否有解. 如果有解,跑拓扑排序就能找出一 ...
- POJ3683 Falsita
http://poj.org/problem?id=3683 思路:2-SAT,输出任意一组方案,O(m+n) #include<cstdio> #include<iostream& ...
- 【POJ3683】Priest John's Busiest Day
题目 John is the only priest in his town. September 1st is the John's busiest day in a year because th ...
- Poj3683:Priest John's Busiest Day
题意 n对夫妻要结婚,第i对夫妻结婚的婚礼持续时间为[Si, Ti],他们会举行一个仪式,仪式时间为Di,这个仪式只能举行在开头或者结尾举行,要么[Si, Si+Di],要么[Ti-Di, Ti],然 ...
- POJ3683 Priest John's Busiest Day(2-SAT)
Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 11049 Accepted: 3767 Special Judge ...
- poj3683(2-SAT 求任意方案)
基础的2-SAT求任意方案的题目. Priest John's Busiest Day Time Limit: 2000MS Memory Limit: 65536K Total Submissi ...
随机推荐
- struts2 之 ServletAPI
1. 在struts2中有两种方式使用SercletAPI,一种解耦方式,一种耦合方式. 2. 解耦方式就是使用ActionContext 来实现,是完全解耦 servletAPI. ActionCo ...
- open vswitch常用操作
以下操作都需要root权限运行,在所有命令中br0表示网桥名称,eth0为网卡名称. 添加网桥: #ovs-vsctl add-br br0 列出open vswitch中的所有网桥: #ovs-vs ...
- 跟Microsoft.AspNet.Identity学习哈希加盐法
什么是哈希加盐法? 废话少说:对于MD5这种加密算法,同样的密码每次加密后得到的密文是一样的,所以黑客可以利用已知的密码库(彩虹库)对目标数据库密文进行对比进行攻击. 怎样解决:哈希加盐法,以下是网上 ...
- JS模式--状态模式(状态机)
下面的状态机选择通过Function.prototype.call方法直接把请求委托给某个字面量对象来执行. var light = function () { this.currstate = FS ...
- Laravel 5.2 教程 - 邮件
一.简介 Laravel 的邮件功能基于热门的 SwiftMailer 函数库之上,提供了一个简洁的 API.Laravel为SMTP.Mailgun.Mandrill.Amazon SES.PHP的 ...
- SVM流行库LIBSvm的使用和调参
简介:Libsvm is a simple, easy-to-use, and efficient software for SVM classification and regression. It ...
- 运算符重载 与 sort()
运算符重载与sort() 二话不说上代码: #include <iostream> #include <algorithm> using namespace std; stru ...
- lock invoke 死锁事例
代码如下: using System; using System.Collections.Generic; using System.Windows.Forms; using System.Threa ...
- linux下fdisk分区管理、文件系统管理、挂载文件系统等
分区管理工具有:fdisk, parted, sfdisk fdisk:对于一块硬盘来讲,最多只能管理15分区: # fdisk -l [-u] [device...] 查看硬盘设备分区信息 # f ...
- 关于爬楼梯的lintcode代码
讲真的,这个我只会用递归去做,但是lintcode上面超时,所以只有在网上找了个动态规划的,虽然这个程序懂了,但是我觉得还是挺不容易的真正弄懂的话-- class Solution {public: ...