Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 802 Accepted Submission(s): 158
和平委员会
根据宪法,Byteland民主共和国的公众和平委员会应该在国会中通过立法程序来创立。 不幸的是,由于某些党派代表之间的不和睦而使得这件事存在障碍。
此委员会必须满足下列条件:
- 每个党派都在委员会中恰有1个代表,
- 如果2个代表彼此厌恶,则他们不能都属于委员会。
每个党在议会中有2个代表。代表从1编号到2n。 编号为2i-1和2i的代表属于第I个党派。
任务
写一程序:
- 从文本文件读入党派的数量和关系不友好的代表对,
- 计算决定建立和平委员会是否可能,若行,则列出委员会的成员表,
- 结果写入文本文件。
输入
在文本文件的第一个行有2非负整数n和m。 他们各自表示:党派的数量n,1 < =n < =8000和不友好的代表对m,0 <=m <=20000。 在下面m行的每行为一对整数a,b,1<=a <b<=2n,中间用单个空格隔开。 它们表示代表a,b互相厌恶。
输出
如果委员会不能创立,文本文件中应该包括单词NIE。若能够成立,文本文件SPO.OUT中应该包括n个从区间1到2n选出的整数,按升序写出,每行一个,这些数字为委员会中代表的编号。如果委员会能以多种方法形成,程序可以只写他们的某一个。
样品输入
3 2 1 3 2 4样品输出
1 4 5
/* HDU 1814 求出字典序最小的解 C++ 2652ms 2316K */ #include<stdio.h> #include<iostream> #include<algorithm> #include<iostream> using namespace std; const int MAXN=16010; const int MAXM=100000; struct Node { int a,b,pre,next; }E[MAXM],E2[MAXM]; int _n,n,m; int V[MAXN],ST[MAXN][2],Q[MAXN],Q2[MAXN],vst[MAXN]; bool res_ex; void init_d() { for(int i=0;i<n;i++) E[i].a=E[i].pre=E[i].next=E2[i].a=E2[i].pre=E2[i].next=i; m=n; } void add_edge(int a,int b) { E[m].a=a;E[m].b=b;E[m].pre=E[a].pre;E[m].next=a;E[a].pre=m;E[E[m].pre].next=m; E2[m].a=b;E2[m].b=a;E2[m].pre=E2[b].pre;E2[m].next=b;E2[b].pre=m;E2[E2[m].pre].next=m; m++; } void solve() {//1 for(int i=0;i<n;i++) { V[i]=0; vst[i]=0; } res_ex=1; int i,i1,i2,j,k,front,rear,front2,rear2; int len; bool ff; for(int _i=0;_i<_n;_i++) {//2 if(V[_i<<1]==1||V[(_i<<1)+1]==1)continue;//找一对未确定的点 i=_i<<1;len=0; if(!V[i]) {//3 ST[len][0]=i; ST[len++][1]=1; if(!V[i^1]) { ST[len][0]=i^1; ST[len++][1]=2; } Q[front=rear=0]=i; vst[i]=i1=n+i; Q2[front2=rear2=0]=i^1; vst[i^1]=i2=(n<<1)+i; ff=1; for(;front<=rear;front++) {//4 j=Q[front]; for(int p=E[j].next;p!=j;p=E[p].next) {//5 k=E[p].b; if(V[k]==2||vst[k]==i2||V[k^1]==1||vst[k^1]==i1) {ff=0;break;} if(vst[k]!=i1) {//6 Q[++rear]=k;vst[k]=i1; if(!V[k]) { ST[len][0]=k; ST[len++][1]=1; } }//6 if(vst[k^1]!=i2) {//6 Q2[++rear2]=k^1;vst[k^1]=i2; if(!V[k]) { ST[len][0]=k^1; ST[len++][1]=2; } }//6 }//5 if(!ff)break; }//4 if(ff) {//4 for(;front2<=rear2;front2++) {//5 j=Q2[front2]; for(int p=E2[j].next;p!=j;p=E2[p].next) {//6 k=E2[p].b; if(V[k]==1||vst[k]==i1) {ff=0;break;} if(vst[k]!=i2) { vst[k]=i2;Q2[++rear]=k; if(!V[k]) { ST[len][0]=k; ST[len++][1]=2; } } }//6 if(!ff)break; }//5 if(ff) { for(int j=0;j<len;j++)V[ST[j][0]]=ST[j][1]; continue; } }//4 }//3 i=(_i<<1)+1;len=0; //******************************************** //下面这段和上面完全一样的,可以复制。但是要保证上面写对 //******************************************** if(!V[i]) {//3 ST[len][0]=i; ST[len++][1]=1; if(!V[i^1]) { ST[len][0]=i^1; ST[len++][1]=2; } Q[front=rear=0]=i; vst[i]=i1=n+i; Q2[front2=rear2=0]=i^1; vst[i^1]=i2=(n<<1)+i; ff=1; for(;front<=rear;front++) {//4 j=Q[front]; for(int p=E[j].next;p!=j;p=E[p].next) {//5 k=E[p].b; if(V[k]==2||vst[k]==i2||V[k^1]==1||vst[k^1]==i1) {ff=0;break;} if(vst[k]!=i1) {//6 Q[++rear]=k;vst[k]=i1; if(!V[k]) { ST[len][0]=k; ST[len++][1]=1; } }//6 if(vst[k^1]!=i2) {//6 Q2[++rear2]=k^1;vst[k^1]=i2; if(!V[k]) { ST[len][0]=k^1; ST[len++][1]=2; } }//6 }//5 if(!ff)break; }//4 if(ff) {//4 for(;front2<=rear2;front2++) {//5 j=Q2[front2]; for(int p=E2[j].next;p!=j;p=E2[p].next) {//6 k=E2[p].b; if(V[k]==1||vst[k]==i1) {ff=0;break;} if(vst[k]!=i2) { vst[k]=i2;Q2[++rear]=k; if(!V[k]) { ST[len][0]=k; ST[len++][1]=2; } } }//6 if(!ff)break; }//5 if(ff) { for(int j=0;j<len;j++)V[ST[j][0]]=ST[j][1]; continue; } }//4 }//3 //************************************************************** if(V[_i<<1]+V[(_i<<1)+1]!=3){res_ex=0;break;} }//2 }//1 //点的编号必须从0开始,2*i和2*i+1是一对sat int main() { int M; int x,y; while(scanf("%d%d",&_n,&M)!=EOF) { n=_n<<1; init_d(); while(M--) { scanf("%d%d",&x,&y); x--; y--; if(x!=(y^1)) { add_edge(x,y^1); add_edge(y,x^1); } } solve(); if(res_ex) { for(int i=0;i<n;i++)//V为0为不确定,1为确定选择,2为确定不选择 if(V[i]==1)printf("%d\n",i+1); } else printf("NIE\n"); } return 0; }
下面写了一个不是字典序的。随便输出的一组。在另外的OJ上AC了的。
http://acm.hit.edu.cn/hoj/problem/view?id=1917
/* HDU 1814 任意输出一组可行解。 在HDU上不能AC,HDU 上要求字典序最小的解 */ //2-SAT问题 //求出所有强连通分量,如果有矛盾点同处于一个连通分量则无解 //缩点,将原图反向建立DAG图//按拓扑排序着色,找一个未着色点x,染成红色 //将与x矛盾的顶点及其子孙染为蓝色 //直到所有顶点均被染色,红色即为2-SAT的一组解 //点的编号从1开始,2*i和2*i+1是一组的 #include<stdio.h> #include<iostream> #include<algorithm> #include<vector> #include<queue> #include<string.h> using namespace std; const int MAXN=16010;//8000*2 char color[MAXN];//染色 bool visit[MAXN]; queue<int>q1,q2; //vector建图方法很妙 vector<vector<int> >adj; //原图 //中间一定要加空格把两个'>'隔开 vector<vector<int> >radj;//逆图 vector<vector<int> >dag;//缩点后的逆向DAG图 int n,m,cnt; int id[MAXN],order[MAXN],ind[MAXN];//强连通分量,访问顺序,入度 void dfs(int u) { visit[u]=true; int i,len=adj[u].size(); for(i=0;i<len;i++) if(!visit[adj[u][i]]) dfs(adj[u][i]); order[cnt++]=u; } void rdfs(int u) { visit[u]=true; id[u]=cnt; int i,len=radj[u].size(); for(i=0;i<len;i++) if(!visit[radj[u][i]]) rdfs(radj[u][i]); } void korasaju() { int i; memset(visit,false,sizeof(visit)); for(cnt=0,i=1;i<=2*n;i++) if(!visit[i]) dfs(i); memset(id,0,sizeof(id)); memset(visit,false,sizeof(visit)); for(cnt=0,i=2*n-1;i>=0;i--) if(!visit[order[i]]) { cnt++;//这个一定要放前面来 rdfs(order[i]); } } bool solvable() { for(int i=1;i<=n;i++) if(id[2*i-1]==id[2*i]) return false; return true; } void topsort() { int i,j,len,now,p,pid; while(!q1.empty()) { now=q1.front(); q1.pop(); if(color[now]!=0)continue; color[now]='R'; ind[now]=-1; for(i=1;i<=2*n;i++) { if(id[i]==now) { p=(i%2)?i+1:i-1; pid=id[p]; q2.push(pid); while(!q2.empty()) { pid=q2.front(); q2.pop(); if(color[pid]=='B')continue; color[pid]='B'; len=dag[pid].size(); for(j=0;j<len;j++) q2.push(dag[pid][j]); } } } len=dag[now].size(); for(i=0;i<len;i++) { ind[dag[now][i]]--; if(ind[dag[now][i]]==0) q1.push(dag[now][i]); } } } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int i,j,x,y,xx,yy,len; while(scanf("%d%d",&n,&m)!=EOF) { adj.assign(2*n+1,vector<int>()); radj.assign(2*n+1,vector<int>()); for(i=0;i<m;i++) { scanf("%d%d",&x,&y); xx=(x%2)?x+1:x-1; yy=(y%2)?y+1:y-1; adj[x].push_back(yy); adj[y].push_back(xx); radj[yy].push_back(x); radj[xx].push_back(y); } korasaju(); if(!solvable())printf("NIE\n"); else { dag.assign(cnt+1,vector<int>()); memset(ind,0,sizeof(ind)); memset(color,0,sizeof(color)); for(i=1;i<=2*n;i++) { len=adj[i].size(); for(j=0;j<len;j++) if(id[i]!=id[adj[i][j]]) { dag[id[adj[i][j]]].push_back(id[i]); ind[id[i]]++; } } for(i=1;i<=cnt;i++) if(ind[i]==0) q1.push(i); topsort(); for(i=1;i<=n;i++)//小心别写错,是color[id[ if(color[id[2*i-1]]=='R')printf("%d\n",2*i-1); else printf("%d\n",2*i); } } } /* 根据宪法,Byteland民主共和国的公众和平委员会应该在国会中通过立法程序来创立。 不幸的是,由于某些党派代表之间的不和睦而使得这件事存在障碍。 此委员会必须满足下列条件: ■每个党派都在委员会中恰有1个代表, ■如果2个代表彼此厌恶,则他们不能都属于委员会。 每个党在议会中有2个代表。代表从1编号到2n。 编号为2i-1和2i的代表属于第I个党派。 任务 写一程序: ■从文本文件读入党派的数量和关系不友好的代表对, ■计算决定建立和平委员会是否可能,若行,则列出委员会的成员表, ■结果写入文本文件。 输入 在文本文件的第一个行有2非负整数n和m。 他们各自表示:党派的数量n,1 < =n < =8000和不友好的代表对m,0 <=m <=20000。 在下面m行的每行为一对整数a,b,1<=a <b<=2n,中间用单个空格隔开。 它们表示代表a,b互相厌恶。 输出 如果委员会不能创立,文本文件中应该包括单词NIE。若能够成立,文本文件SPO.OUT中应该包括n个从区间1到2n选出的整数,按升序写出,每行一个,这些数字为委员会中代表的编号。如果委员会能以多种方法形成,程序可以只写他们的某一个。 样品输入 3 2 1 3 2 4样品输出 1 4 5 */