http://poj.org/problem?id=3683
标准的2-sat 而且是需要输出路径的
一直纠结 缩点后建反向图 再利用拓扑排序那一点到底是怎么弄的 原来是自己的拓扑排序没学好 晕
还有刚开始学邻接表的时候一直用动态的 就是没加一条边都要申请一个 new node
没想到这次就超时了 因为边太多了 改成静态的直接100ms+ 差距太大了 以后不敢再用动态的了
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#define LL long long
using namespace std;
const int N=2005;
vector<int>ve[2005];//保存环
int head1[N],I1;
int head2[N],I2;
struct ss1
{
int j,next;
}side1[N*1000];//初始邻接表
struct ss2
{
int j,next;
}side2[N*1000];//缩点后 反向邻接表
struct T
{
int st,et;
}setime[N];
int dfn[N];
int low[N];
bool in[N];
bool visited[N];
int deep;
int f[N];
stack<int>str;
queue<int>qt;
int num[N];//拓扑排序计数
int sele[N];//选择 0 为未选 1为选 -1为不选
void build(int i,int j)
{
side1[I1].j=j;
side1[I1].next=head1[i];
head1[i]=I1++;
}
void rebuild(int i,int j)
{
side2[I2].j=j;
side2[I2].next=head2[i];
head2[i]=I2++;
}
void Tarjan(int x)//判环 缩点
{
visited[x]=true;
str.push(x);
in[x]=true;
dfn[x]=low[x]=deep++;
int t=head1[x];
while(t!=-1)
{
int k=side1[t].j;
if(visited[k]==false)
{
Tarjan(k);
low[x]=min(low[x],low[k]);
}else if(in[k]==true)
{
low[x]=min(low[x],dfn[k]);
}
t=side1[t].next;
}
if(low[x]==dfn[x])
{
while(str.top()!=x)
{
int k=str.top();
str.pop();
in[k]=false;
f[k]=x;
ve[x].push_back(k);
}
int k=str.top();
str.pop();
in[k]=false;
f[k]=x;
}
}
bool same(int i,int j)//时间是否冲突
{
if(setime[i].et<=setime[j].st)
return false;
if(setime[j].et<=setime[i].st)
return false;
return true;
}
void fsearch(int x)//反向建邻接表 只建缩点
{
int t=head1[x];
while(t!=-1)
{
int k=side1[t].j;
if(f[x]!=f[k])
{
rebuild(f[k],f[x]);
++num[f[x]];//拓扑排序 计数
}
t=side1[t].next;
}
}
void subside(int x)//拓扑中 用掉一个边 将它指向的点的计数减一 为0的话 入队列
{
int t=head2[x];
while(t!=-1)
{
--num[side2[t].j];
if(num[side2[t].j]==0)
qt.push(side2[t].j);
t=side2[t].next;
}
}
void color(int k,int n)//将一个环内的点 然成相同颜色
{
int l1=k;
int l2=(k<n)?k+n:k-n;
l1=f[l1];l2=f[l2];
sele[l1]=1;
for(unsigned int i=0;i<ve[l1].size();++i)
sele[ve[l1][i]]=1;
sele[l2]=-1;
for(unsigned int i=0;i<ve[l2].size();++i)
sele[ve[l2][i]]=-1;
}
void print(int i)//输出
{
int t1=setime[i].st;
int t2=setime[i].et;
printf("%02d:%02d %02d:%02d\n",t1/60,t1%60,t2/60,t2%60);
}
int main()
{
//freopen("data.txt","r",stdin);
int n;
while(scanf("%d",&n)!=EOF)
{
int l1,l2,k1,k2,d;
char c1,c2;
memset(head1,-1,sizeof(head1));
I1=0;
for(int i=0;i<n;++i)
{
scanf("%d%c%d %d%c%d %d",&l1,&c1,&l2,&k1,&c2,&k2,&d);
setime[i].st=l1*60+l2;
setime[i].et=l1*60+l2+d;
setime[i+n].et=k1*60+k2;
setime[i+n].st=k1*60+k2-d;
for(int j=0;j<i;++j)
{
if(same(j,i))//判断是否冲突
{
build(j,i+n);build(i,j+n);
}
if(same(j+n,i))
{
build(j+n,i+n);build(i,j);
}
if(same(j,i+n))
{
build(j,i);build(i+n,j+n);
}
if(same(j+n,i+n))
{
build(j+n,i);build(i+n,j);
}
}
}
while(!str.empty())
str.pop();
memset(dfn,-1,sizeof(dfn));
memset(low,-1,sizeof(low));
memset(in,false,sizeof(in));
memset(visited,false,sizeof(visited));
for(int i=0;i<2*n;++i)
f[i]=i;
deep=0;
for(int i=0;i<n;++i)
ve[i].clear();
int l;
for(l=0;l<n;++l)
{
if(visited[l]==false)
Tarjan(l);
if(f[l]==f[l+n])
break;
}
if(l<n)
printf("NO\n");
else
{
printf("YES\n");
memset(head2,-1,sizeof(head2));
I2=0;
for(int i=0;i<2*n;++i)
fsearch(i);//重新建反向图
for(int i=0;i<2*n;++i)
if(f[i]==i&&num[i]==0)
qt.push(i);//拓扑后为0的进队列
memset(sele,0,sizeof(sele));
while(!qt.empty())
{
int k=qt.front();
qt.pop();
if(sele[k]==0)//为染色的染色
color(k,n);
subside(k);
}
for(int i=0;i<n;++i)
{
if(sele[i]==1)
print(i);
else
print(i+n);
}
}
}
return 0;
}