题意就是最基础的2sat,关系只有矛盾关系,然后二选一,关键是这个题目是输出字典序最小的那组解。
思路:
输出字典序最小,用强连通那个实现不了(起码没看到有人实现),其实我们可以直接暴力,我们可以给某个点染色,分成无色(W)红色(R)和蓝色(B)红色是我们要的答案,对于每一个点我们先尽可能的吧他的a染成红色,把他的~a染成蓝色,如果发现矛盾,就是出现a是蓝色的了,那么我们就把当前这个连通块重新清成W吧其实点换成~a这样在继续染色,如果还是不行,那么就证明无解。否则这样全部染完色之后的红色点就是我们要的点。时间复杂度是 n * m * 2,大约也就n * m左右。本以为会超时,感觉到亿了,结果没有,1140ms AC ,貌似是有直接求什么字典序最小的固定算法吧!反正有也不会。哎!
#include<stdio.h> #include<string.h> #include<queue> #define N_node 16000 + 100 #define N_edge 40000 + 400 #define W 0 #define R 1 #define B 2 using namespace std; typedef struct { int to ,next; }STAR; STAR E[N_edge]; int list[N_node] ,tot; int col[N_node]; queue<int>q; void add(int a ,int b) { E[++tot].to = b; E[tot].next = list[a]; list[a] = tot; } bool DFS(int s) { if(col[s] == B) return 0; if(col[s] == R) return 1; col[s] = R ,col[s^1] = B; q.push(s); for(int k = list[s] ;k ;k = E[k].next) if(!DFS(E[k].to)) return 0; return 1; } bool solve(int n) { memset(col ,W ,sizeof(col)); for(int i = 0 ;i < n ;i ++) { if(col[i]) continue; while(!q.empty()) q.pop(); if(!DFS(i)) { while(!q.empty()) { col[q.front()] = col[q.front()^1] = W; q.pop(); } if(!DFS(i^1)) return 0; } } return 1; } int main () { int n ,m ,i ,a ,b; while(~scanf("%d %d" ,&n ,&m)) { memset(list ,0 ,sizeof(list)) ,tot = 1; for(i = 1 ;i <= m ;i ++) { scanf("%d %d" ,&a ,&b); a-- ,b--; add(a ,b^1) ,add(b ,a^1); } if(solve(n * 2)) { for(i = 1 ;i <= n * 2 ;i ++) if(col[i-1] == R) printf("%d " ,i); } else printf("NIE "); } return 0; }