poj 3648 Wedding+2-sat+SCC+缩点+拓扑排序

来源:互联网 发布:豆豆的背叛电视剧知乎 编辑:程序博客网 时间:2024/05/05 01:44
/*2-sat问题,题意:有对情侣结婚,请来n-1对夫妇,算上他们自己共n对,编号为0~~n-1,他们自己编号为0所有人坐在桌子两旁,新娘不想看到对面的人有夫妻关系或偷奸关系,若有解,输出一组解,无解输出bad luck思路:1.根据偷奸关系建图(1h和2h有偷奸关系,建边1h->2w    2h->1w)2.求强连通分量3.判断有无解(任一对夫妇不在同一强连通分量中,有解;否则无解)4.缩点建图(建反向图)5.拓扑排序6.由底向上求解(由于上面建的是反向图,所以自顶(无入度)向下)至此,选出来的是做新娘对面的,要求输出,坐在新娘一边的*/#include<stdio.h>#include<string.h>#include<queue>#include<stack>using namespace std;struct edge{int yong;int v[1000000];int next[1000000];int head[1000000];edge(){clear();}void clear(){yong=1;memset(head,0,sizeof(head));}void add(int u,int w){v[yong]=w;next[yong]=head[u];head[u]=yong;yong++;}}e1,e2;int dfn[1000],low[1000];int n,m,index,scc;queue<int>q;stack<int>ss;int ins[1000],belong[1000],dui[1000],ind[1000],fang[1000];void tarjan(int u)//tarjan求强连通分量{int i,v;dfn[u]=low[u]=index++;ins[u]=1;ss.push(u);//之前误写成队列for(i=e1.head[u];i;i=e1.next[i]){v=e1.v[i];if(dfn[v]==0){tarjan(v);if(low[v]<low[u])low[u]=low[v];}else if(ins[v]&&dfn[v]<low[u])low[u]=dfn[v];}if(low[u]==dfn[u]){do{v=ss.top();ss.pop();ins[v]=0;belong[v]=scc;//标记所属强连通分量的标号}while(v!=u);scc++;}}void topsort()//拓扑排序  和  求解{int v,u;while(!q.empty())q.pop();int i;for(i=0;i<scc;i++){if(ind[i]==0)//0入度的节点入队q.push(i);}while(!q.empty()){v=q.front();q.pop();if(!fang[v])//0入度的节点涂色{//fang[i]表示标号为i的强连通分量被选择  或  删除fang[v]=1;//表示选择fang[dui[v]]=2;//表示删除}for(i=e2.head[v];i;i=e2.next[i]){u=e2.v[i];if(--ind[u]==0)//减入度,0入度的入队{q.push(u);}}}}int main(){int i,j,a,b,v;char c1,c2;while(scanf("%d%d",&n,&m),m+n){//初始化e1.clear();e2.clear();//建原图for(i=1;i<=m;i++){scanf("%d%c %d%c",&a,&c1,&b,&c2);e1.add(c1=='h'?a*2+1:a*2,c2=='h'?b*2:b*2+1);e1.add(c2=='h'?b*2+1:b*2,c1=='h'?a*2:a*2+1);//这里处理要仔细}e1.add(0,1);//新郎必选//求强连通分量index=1;//dfn[]标记scc=0;//强连通分量个数,从0开始计数memset(dfn,0,sizeof(dfn));memset(ins,0,sizeof(ins));for(i=0;i<2*n;i++){if(!dfn[i])tarjan(i);}//判断有无解for(i=0;i<n;i++){//belong[i]表示i所属的强连通分量的标号if(belong[2*i]==belong[2*i+1])//若有夫妇在同一强连通分量中,无解break;dui[belong[2*i+1]]=belong[2*i];//记录配偶所属的强连通分量的标号,求解的时候有用dui[belong[2*i]]=belong[2*i+1];}if(i!=n){printf("bad luck\n");continue;}//构建反向缩点图memset(ind,0,sizeof(ind));for(i=0;i<2*n;i++){for(j=e1.head[i];j;j=e1.next[j]){v=e1.v[j];if(belong[i]!=belong[v]){e2.add(belong[v],belong[i]);//注意,这里的顺序ind[belong[i]]++;//统计入度}}}//拓扑排序  和  求解  同时进行了memset(fang,0,sizeof(fang));topsort();//输出解for(i=1;i<n;i++){if(fang[belong[2*i]]==1)printf("%dh ",i);else printf("%dw ",i);}printf("\n");}return 0;}

原创粉丝点击