题目大意:
有n队夫妇结婚,需要神父去做一项仪式,仪式持续d分钟,每对夫妇的需要的时间不同,婚礼有开始时间s和结束时间t,仪式必须在婚礼开始和婚礼结束时进行。即在s s+d 或者 t-d t 这两个区间内执行。问神父如何安排使得所有的婚礼不冲突,输出神父每个婚礼执行的时间。
解题思路:
对于每一个婚礼,其实我们有两个选择且必须做出一个选择。分析后就是一个典型的2-SAT问题。把每一个婚礼拆成两个数分别代表开始执行和结束执行。我这里用的是i和i+n,这里特别要注意用的是那种分离点属性的方式,是2*i-1和2*i还是i和i+n,我因为开始有错误导致频繁的换建图方式以至于忘了自己点的分离方式wa了一天没查出来错误。最后改了以后直接A了。。。
Ac代码:
#include<cstdio>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=2e3+5;
const int INF=1e9+7;
int n,opp[maxn];
vector<int> v[maxn];
struct node
{
int s,t,d;
}a[maxn];
int tot,cnt,low[maxn],dfn[maxn],belong[maxn];
bool vis[maxn];
vector<int> r[maxn];
stack<int> s;
void tarjan(int u)
{
low[u]=dfn[u]=++tot;
s.push(u);vis[u]=1;
for(int i=0;i<v[u].size();i++)
{
int g=v[u][i];
if(!dfn[g])
{
tarjan(g);
low[u]=min(low[u],low[g]);
}
else if(vis[g]) low[u]=min(low[u],dfn[g]);
}
if(dfn[u]==low[u])
{
cnt++;
while(!s.empty())
{
int now=s.top();
s.pop();vis[now]=0;
belong[now]=cnt;
if(now==u) break;
}
}
}
void rebuild()
{
for(int i=1;i<=2*n;i++)
{
for(int j=0;j<v[i].size();j++)
{
int u=v[i][j];
if(belong[i]==belong[u]) continue;
r[belong[u]].push_back(belong[i]);
}
}
for(int i=1;i<=cnt;i++)
sort(r[i].begin(),r[i].end()),r[i].erase(unique(r[i].begin(),r[i].end()),r[i].end());
}
void add(int x,int y)
{
v[x].push_back(y);
}
int color[maxn],sl[maxn];
void dfs(int u)
{
for(int i=0;i<r[u].size();i++) //传递不选择标记
{
if(color[r[u][i]]==-1)
{
color[r[u][i]]=0;
dfs(r[u][i]);
}
}
}
void toppsort()
{
for(int i=0;i<=cnt;i++) color[i]=-1; //初始化为1
for(int i=1;i<=cnt;i++)
for(int j=0;j<r[i].size();j++)
sl[r[i][j]]++; //计算每个点的入度
queue<int> que;
for(int i=1;i<=cnt;i++) //拓扑排序
if(sl[i]==0) que.push(i);
while(!que.empty())
{
int now=que.front();
que.pop();
if(color[now]==-1) //没出现过 标记为1
{
color[now]=1;
color[opp[now]]=0; //对立点标记为0
dfs(opp[now]); //传递不选择标记
}
for(int i=0;i<r[now].size();i++)
{
sl[r[now][i]]--;
if(sl[r[now][i]]==0)
que.push(r[now][i]);
}
}
}
void SAT()
{
int flag=0;
for(int i=1;i<=n;i++) //判断一下合法性
{
if(belong[i]==belong[i+n]) flag=1; //这里注意i和i+n 因为这个wa了一天
opp[belong[i]]=belong[i+n]; //保存一下它的对立点
opp[belong[i+n]]=belong[i];
}
if(flag)
{
printf("NO\n");
return ;
}
printf("YES\n");
toppsort(); //执行拓朴排序 染色
for(int i=1;i<=n;i++)
{
if(color[belong[i]]==1)
printf("%.2d:%.2d %.2d:%.2d\n",a[i].s/60,a[i].s%60,(a[i].s+a[i].d)/60,(a[i].s+a[i].d)%60);
else
printf("%.2d:%.2d %.2d:%.2d\n",(a[i].t-a[i].d)/60,(a[i].t-a[i].d)%60,a[i].t/60,a[i].t%60);
}
}
bool check(int s1,int t1,int s2,int t2)
{
if(s1>=t2||s2>=t1) return 0;
return 1;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x1,y1,x2,y2;
scanf("%d:%d %d:%d %d",&x1,&y1,&x2,&y2,&a[i].d); //把时间处理为分钟表示 简化
a[i].s=x1*60+y1;
a[i].t=x2*60+y2;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j) continue;
if(check(a[i].s,a[i].s+a[i].d,a[j].s,a[j].s+a[j].d)) //不能同时选开头
add(i,j+n);
if(check(a[i].s,a[i].s+a[i].d,a[j].t-a[j].d,a[j].t)) //不能同时选i开头和j结尾
add(i,j);
if(check(a[i].t-a[i].d,a[i].t,a[j].s,a[j].s+a[j].d)) //不能同时选i结尾和j开头
add(i+n,j+n);
if(check(a[i].t-a[i].d,a[i].t,a[j].t-a[j].d,a[j].t)) //不能同时选结尾
add(i+n,j);
}
}
for(int i=1;i<=2*n;i++) //缩点
if(!dfn[i]) tarjan(i);
rebuild(); //重建图
SAT();
//system("pause");
return 0;
}