Description
约翰是街区里唯一的神父。假设有n对新人打算在同一天举行结婚仪式。第i对新人的结婚仪式的时间为Si到Ti,在其仪式开始时或是结束时需要进行一个用时为Di的特别仪式(也就是从Si到Si+Di或是从Ti-Di到Ti),该特别仪式需要神父在场。请判断是否可以通过合理安排每个仪式在开始时或是在结束时进行,从而保证神父能够出席所有的特别仪式。如果可能的话,请输出出席各个特别仪式的时间。当然,神父不可能出席多个仪式,不过神父前往仪式的途中所花费的时间可以忽略不计,神父可以在出席完一个特别仪式后,立即出席另一个开始时间与其结束时间相等的特别仪式
Input
第一行为一个整数n表示举行结婚仪式的新人对数,之后n行每行为每个仪式对应的Si,Ti与Di
Output
如果存在可行方案,输出YES并输出出席各个特别仪式的时间,否则输出NO
Sample Input
2
08:00 09:00 30
08:15 09:00 20
Sample Output
YES
08:00 08:30
08:40 09:00
Solution
2-SAT判断可行性,定义变量Xi为在开始时进行第i组新人的特别仪式
如果Si~Si+Di与Sj~Sj+Dj冲突,则Xi->~Xj,Xj->~Xi
如果Si~Si+Di与Ti -Di~Ti冲突,则Xi->Xj,~Xj->~Xi
如果Ti -Di~Ti与Sj~Sj+Dj冲突,则Xj->Xi,~Xi->~Xj
如果Ti -Di~Ti与Tj -Dj~Tj冲突,则~Xi->Xj,~Xj->Xi
输出任意解的两种方法都写了写, 判断两个区间是否相交+输出路径
拓扑排序的
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e3 + 5;
int n, m, low[maxn], dfn[maxn], id[maxn], scc_cnt, dfs_cnt, cnt[maxn];
int S[maxn], T[maxn], D[maxn], in[maxn], col[maxn], opp[maxn];
vector<int> v[maxn], vt[maxn];
stack<int> s;
void init()
{
memset(low, 0, sizeof(low));
memset(id, 0, sizeof(id));
memset(dfn, 0, sizeof(dfn));
memset(in, 0, sizeof(in));
memset(col, -1, sizeof(col));
memset(opp, 0, sizeof(opp));
scc_cnt = dfs_cnt = 0;
for(int i = 0; i < maxn; i++)
v[i].clear();
while(!s.empty())
s.pop();
}
void addedge(int x, int y)
{
v[x].push_back(y);
}
void addedge2(int x, int y)
{
vt[x].push_back(y);
}
void tarjan(int x)
{
dfn[x] = low[x] = ++dfs_cnt;
s.push(x);
for(int i = 0; i < v[x].size(); i++)
{
int to = v[x][i];
if(!dfn[to])
{
tarjan(to);
low[x] = min(low[x], low[to]);
}
else if(!id[to])
low[x] = min(low[x], dfn[to]);
}
if(low[x] == dfn[x])
{
scc_cnt++;
while(1)
{
int u = s.top();
s.pop();
id[u] = scc_cnt;
if(x == u) break;
}
}
}
void scc()
{
for(int i = 0; i < 2*n ; i++)
if(!dfn[i])
tarjan(i);
}
int Q[maxn];
void top()
{
memset(in, 0, sizeof(in));
memset(col, -1, sizeof(col));
for(int i = 0; i < 2*n; i++) vt[i].clear();
for(int i = 0; i < 2*n; i++)
{
for(int j = 0; j < v[i].size(); j++)
{
int to = v[i][j];
if(id[i] == id[to]) continue; // 在同一个连通分量不用考虑
addedge2(id[to], id[i]); //反着存边
in[id[i]]++; //入度++
}
}
int frnt, rear;
frnt = rear = 0;
// cout << scc_cnt << endl;
for(int i = 1; i <= scc_cnt; i++)
if(!in[i]) Q[rear++] = i;
while(frnt != rear)
{
int u = Q[frnt++];
if(col[u] == -1) //未染色
{
col[u] = 1; //染色1
col[opp[u]] = 0; //对立点染色0
}
for(int i = 0; i < vt[u].size(); i++)
{
int to = vt[u][i];
if(--in[to] == 0)
Q[rear++] = to;
}
}
}
int main()
{
while(~scanf("%d", &n))
{
init();
for(int i = 0; i < n; i++)
{
int s1, s2, t1, t2;
scanf("%d:%d%d:%d%d", &s1, &s2, &t1, &t2, &D[i]);
S[i] = s1*60 + s2;
T[i] = t1*60 + t2;
}
for(int i = 0; i < n; i++)
for(int j = i+1; j < n; j++)
{
if(min(S[i]+D[i], S[j]+D[j]) > max(S[i], S[j])) // 开头两个区间相交
{
addedge(i, j+n); //i 跟 j不能共存, 那么选i就一定选 j+n
addedge(j, i+n); //反过来一样
}
if(min(S[i]+D[i], T[j]) > max(S[i], T[j]-D[j]))
{
addedge(i, j); // i 跟 j+n冲突 那么i一定选j
addedge(j+n, i+n);
}
if(min(S[j]+D[j], T[i]) > max(S[j], T[i]-D[i]))
{
addedge(j, i);
addedge(i+n, j+n);
}
if(min(T[i], T[j]) > max(T[i]-D[i], T[j]-D[j]))
{
addedge(i+n, j);
addedge(j+n, i);
}
}
scc();
int flag = 0;
for(int i = 0; i < n; i++)
{
if(id[i] == id[i+n])
{
flag = 1;
break;
}
opp[id[i]] = id[i+n];
opp[id[i+n]] = id[i];
}
if(flag)
{
printf("NO\n");
continue;
}
puts("YES");
top();
// for(int i = 0; i < n; i++)
// cout <<
for(int i = 0; i < n; i++)
{
if(col[id[i]] == 1)
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;
}
直接scc的
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e3 + 5;
int n, m, low[maxn], dfn[maxn], id[maxn], scc_cnt, dfs_cnt, cnt[maxn];
int S[maxn], T[maxn], D[maxn];
vector<int> v[maxn];
stack<int> s;
void init()
{
memset(low, 0, sizeof(low));
memset(id, 0, sizeof(id));
memset(dfn, 0, sizeof(dfn));
scc_cnt = dfs_cnt = 0;
for(int i = 0; i < maxn; i++)
v[i].clear();
while(!s.empty())
s.pop();
}
void addedge(int x, int y)
{
v[x].push_back(y);
}
void tarjan(int x)
{
dfn[x] = low[x] = ++dfs_cnt;
s.push(x);
for(int i = 0; i < v[x].size(); i++)
{
int to = v[x][i];
if(!dfn[to])
{
tarjan(to);
low[x] = min(low[x], low[to]);
}
else if(!id[to])
low[x] = min(low[x], dfn[to]);
}
if(low[x] == dfn[x])
{
scc_cnt++;
while(1)
{
int u = s.top();
s.pop();
id[u] = scc_cnt;
if(x == u) break;
}
}
}
void scc()
{
for(int i = 0; i < 2*n ; i++)
if(!dfn[i])
tarjan(i);
}
int main()
{
while(~scanf("%d", &n))
{
init();
for(int i = 0; i < n; i++)
{
int s1, s2, t1, t2;
scanf("%d:%d%d:%d%d", &s1, &s2, &t1, &t2, &D[i]);
S[i] = s1*60 + s2;
T[i] = t1*60 + t2;
}
for(int i = 0; i < n; i++)
for(int j = i+1; j < n; j++)
{
if(min(S[i]+D[i], S[j]+D[j]) > max(S[i], S[j])) // 开头两个区间相交
{
addedge(i, j+n); //i 跟 j不能共存, 那么选i就一定选 j+n
addedge(j, i+n); //反过来一样
}
if(min(S[i]+D[i], T[j]) > max(S[i], T[j]-D[j]))
{
addedge(i, j); // i 跟 j+n冲突 那么i一定选j
addedge(j+n, i+n);
}
if(min(S[j]+D[j], T[i]) > max(S[j], T[i]-D[i]))
{
addedge(j, i);
addedge(i+n, j+n);
}
if(min(T[i], T[j]) > max(T[i]-D[i], T[j]-D[j]))
{
addedge(i+n, j);
addedge(j+n, i);
}
}
scc();
int flag = 0;
for(int i = 0; i < n; i++)
{
if(id[i] == id[i+n])
{
flag = 1;
break;
}
}
if(flag)
{
printf("NO\n");
continue;
}
puts("YES");
for(int i = 0; i < n; i++)
{
if(id[i] < id[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);
}
}
return 0;
}