POJ 3683 2SAT
这道题涨了很多姿势。
首先,以后可能不需要重新建图+拓扑排序+染色了。
看了赵爽的论文。
下面说的简略一些,详细情况可自行百度。
首先是重新建图,重新建图为的是求拓扑序,以便于染色,出答案。而本身我们tarjan完的就是个拓扑逆序的。因为每个团求出来之前,它之后的所有团肯定都求出来,否则这个团是全都在栈里的。
所以,拓扑序和重新建图是不需要的。然后是染色,染色是为了出答案。
关于可行性判定就不说了,观察答案,考虑到,每对点都要被挑一个(否则不可行),而且肯定是挑拓扑序小的那个,因为大的是永远不可能被选到的。分情况证明,首先,如果挑的时候,序小的和大的两团都在,那自然挑大的;小的在,大的不在,自然挑小的;大的在,小的不在,这种情况不可能出现,因为如果大的在之前被排除掉了,那么序小的这个肯定在之前就被挑过了,因为有证明,如果i,j在一个团里,那么i',j'也在一个团里。
下面是代码,以后可以更进一步了。
#include<algorithm> #include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<stack> #include<cmath> #include<cstdlib> using namespace std; const int MAXN=2010; struct marry { int fr; int ed; }date[MAXN]; struct node { int v; int nxt; }; node edge[MAXN*MAXN];//边数 int head[MAXN]; int n,m; int Stop,Bcnt,Dindex;//栈头,强通块数,时间戳 int DFN[MAXN],LOW[MAXN];//首时间戳,最近回溯点(根) int Stap[MAXN];//答案栈 int instack[MAXN];//是否在栈中 int Belong[MAXN];//这个点属于第几个强连通块(点) int cnt=0; void add_edge(int u,int v) { edge[cnt].v=v; edge[cnt].nxt=head[u]; head[u]=cnt; cnt++; } void tarjan(int i) { int j; DFN[i]=LOW[i]=++Dindex; instack[i]=1; Stap[++Stop]=i; for (int e=head[i]; e!=-1; e=edge[e].nxt) { j=edge[e].v; if (!DFN[j])//儿子没遍历 { tarjan(j);//遍历 if (LOW[j]<LOW[i])//如果儿子已经形成环 LOW[i]=LOW[j];//父亲也要在回溯的时候进入环 } else if (instack[j]&&DFN[j]<LOW[i])//邻接的在栈里,所以是大大 LOW[i]=DFN[j];//把这个点归到大大那 } if (DFN[i]==LOW[i])//这个点的根是自己 { Bcnt++;//多了一个强连通分量 do { j=Stap[Stop--];//退栈 instack[j]=0;//标记 Belong[j]=Bcnt;//标记 } while (j!=i); } } int solve() { int i; Stop=Bcnt=Dindex=0;//栈头,强通块数,时间戳 memset(DFN,0,sizeof(DFN)); for (int i=0; i<2*n; i++) { // printf ("dfn[i]\%d\n",DFN[i]); if (!DFN[i]) tarjan(i); } for (int i=0;i<2*n;i+=2)//不在一个联通块里 { if (Belong[i]==Belong[i+1]) return 0; } return 1; } int against(marry a,marry b) { if (a.fr<=b.fr&&a.ed>b.fr) return 1; if (a.fr>=b.fr&&a.fr<b.ed) return 1; return 0; } int main() { while (scanf ("%d",&n)!=EOF) { for (int i=0;i<2*n;i+=2) { int h1,m1,h2,m2,tmp; scanf ("%d:%d%d:%d%d",&h1,&m1,&h2,&m2,&tmp); date[i].fr=h1*60+m1; date[i].ed=h1*60+m1+tmp; date[i+1].fr=h2*60+m2-tmp; date[i+1].ed=h2*60+m2; } memset(head,-1,sizeof(head)); for (int i=0;i<2*n;i++) { for (int j=i+1;j<2*n;j++) { if (i!=(j^1)&&against(date[i],date[j])) { add_edge(i,j^1); add_edge(j,i^1); } } } if (!solve()) printf ("NO\n"); else { printf ("YES\n"); for (int i=0;i<2*n;i+=2) { if (Belong[i]<Belong[i+1]) { printf ("%02d:%02d %02d:%02d\n",date[i].fr/60,date[i].fr%60,date[i].ed/60,date[i].ed%60); } else printf ("%02d:%02d %02d:%02d\n",date[i+1].fr/60,date[i+1].fr%60,date[i+1].ed/60,date[i+1].ed%60); } } } return 0; }