题意:
题目大意:某一天结婚的人特别多但是主持婚礼的神父只有一个。婚礼时间从s开始到e结束,神父必须在s到s+d或者e-d到e这段时间内在。给定了n个婚礼的s,e,d,求一种方案能使得神父主持所有的婚礼。
明显是2-SAT问题,把靠近前面区间定义为0,后面定义为1,则n^2先连边建图,判断是否存在则是i与i+n是否在同一scc
难点在于构造方案:
第一个方法首先缩点,其实是要在原图上从0出节点开始选择染色,因为0出节点不会对别的点造成影响,所以原图拓扑序中,若x的拓扑序更大,则x先为真,所以缩点后建反图,然后正常从0入点进行拓扑序染色即可,
第二种和第一种思路一样,只是简便了一点,注意到求强连通分量tarjan时,scc的编号其实就是缩点自底向上的拓扑序,所以只要i和i+n谁的编号更小就先选择谁
以下给出两个代码
第一种
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int u = 2010, w = 3000010;
int ver[w], Next[w], head[u], dfn[u], low[u], c[u], s[u], ins[u];
int ver2[w], Next2[w], head2[u], val[u], deg[u], opp[u];
int S[u], T[u], D[u], ex[w], ey[w];
int n, m, tot, tot2, num, t, p, e;
queue<int> q;
// 原图加边
void add(int x, int y) {
ver[++tot] = y, Next[tot] = head[x], head[x] = tot;
ex[++e] = x, ey[e] = y;
}
// 缩点后的图加边
void add2(int x, int y) {
ver2[++tot2] = y, Next2[tot2] = head2[x], head2[x] = tot2;
}
void tarjan(int x) {
dfn[x] = low[x] = ++num;
s[++p] = x, ins[x] = 1;
for (int i = head[x]; i; i = Next[i])
if (!dfn[ver[i]]) {
tarjan(ver[i]);
low[x] = min(low[x], low[ver[i]]);
}
else if (ins[ver[i]])
low[x] = min(low[x], low[ver[i]]);
if (dfn[x] == low[x]) {
t++; int y;
do { y = s[p--], ins[y] = 0; c[y] = t; } while (x != y);
}
}
void topsort() {
memset(val, -1, sizeof(val));
// 缩点,建反图
for (int i = 1; i <= e; i++)
if (c[ex[i]] != c[ey[i]])
add2(c[ey[i]], c[ex[i]]), deg[c[ex[i]]]++;
// 零入度点入队
for (int i = 1; i <= t; i++)
if (!deg[i]) q.push(i);
// 拓扑排序
while (q.size()) {
int k = q.front(); q.pop();
// 赋值标记
if (val[k] == -1) val[k] = 0, val[opp[k]] = 1;
for (int i = head2[k]; i; i = Next2[i])
if (--deg[ver2[i]] == 0) q.push(ver2[i]);
}
// 输出最终结果
for (int i = 1; i <= n; i++)
if (val[c[i]] == 0) 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);
}
bool overlap(int a, int b, int c, int d) {
if (a >= c&&a<d || b>c&&b <= d || a <= c&&b >= d) return 1;
return 0;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
int sh, sm, th, tm;
scanf("%d:%d %d:%d %d", &sh, &sm, &th, &tm, &D[i]);
S[i] = sh * 60 + sm; T[i] = th * 60 + tm;
}
for (int i = 1; i < n; i++)
for (int j = i + 1; j <= n; j++) {
if (overlap(S[i], S[i] + D[i], S[j], S[j] + D[j]))
add(i, n + j), add(j, n + i);
if (overlap(S[i], S[i] + D[i], T[j] - D[j], T[j]))
add(i, j), add(n + j, n + i);
if (overlap(T[i] - D[i], T[i], S[j], S[j] + D[j]))
add(n + i, n + j), add(j, i);
if (overlap(T[i] - D[i], T[i], T[j] - D[j], T[j]))
add(n + i, j), add(n + j, i);
}
for (int i = 1; i <= 2 * n; i++)
if (!dfn[i]) tarjan(i);
for (int i = 1; i <= n; i++) {
if (c[i] == c[n + i]) { puts("NO"); return 0; }
opp[c[i]] = c[n + i], opp[c[n + i]] = c[i];
}
puts("YES");
topsort();
return 0;
}
第二种构造
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int u = 2010, w = 3000010;
int ver[w], Next[w], head[u], dfn[u], low[u], c[u], s[u], ins[u];
int val[u], deg[u], opp[u], S[u], T[u], D[u];
int n, m, tot, num, t, p;
// 原图加边
void add(int x, int y) {
ver[++tot] = y, Next[tot] = head[x], head[x] = tot;
}
void tarjan(int x) {
dfn[x] = low[x] = ++num;
s[++p] = x, ins[x] = 1;
for (int i = head[x]; i; i = Next[i])
if (!dfn[ver[i]]) {
tarjan(ver[i]);
low[x] = min(low[x], low[ver[i]]);
}
else if (ins[ver[i]])
low[x] = min(low[x], low[ver[i]]);
if (dfn[x] == low[x]) {
t++; int y;
do { y = s[p--], ins[y] = 0; c[y] = t; } while (x != y);
}
}
bool overlap(int a, int b, int c, int d) {
if (a >= c&&a<d || b>c&&b <= d || a <= c&&b >= d) return 1;
return 0;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
int sh, sm, th, tm;
scanf("%d:%d %d:%d %d", &sh, &sm, &th, &tm, &D[i]);
S[i] = sh * 60 + sm; T[i] = th * 60 + tm;
}
for (int i = 1; i < n; i++)
for (int j = i + 1; j <= n; j++) {
if (overlap(S[i], S[i] + D[i], S[j], S[j] + D[j]))
add(i, n + j), add(j, n + i);
if (overlap(S[i], S[i] + D[i], T[j] - D[j], T[j]))
add(i, j), add(n + j, n + i);
if (overlap(T[i] - D[i], T[i], S[j], S[j] + D[j]))
add(n + i, n + j), add(j, i);
if (overlap(T[i] - D[i], T[i], T[j] - D[j], T[j]))
add(n + i, j), add(n + j, i);
}
for (int i = 1; i <= 2 * n; i++)
if (!dfn[i]) tarjan(i);
for (int i = 1; i <= n; i++) {
if (c[i] == c[n + i]) { puts("NO"); return 0; }
opp[i] = n + i, opp[n + i] = i;
}
puts("YES");
// 构造方案
for (int i = 1; i <= 2 * n; i++)
val[i] = c[i] > c[opp[i]];
// 输出最终结果
for (int i = 1; i <= n; i++)
if (val[i] == 0) 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);
return 0;
}