/** * 题目大意为有m人候选人,n个选举人,每个人有两个选择, * 例如(+3, -1) +3表示选第3个人, -1表示不选第一个人, * 要求最终选举出来的结果至少符合每个人的其中一种选择 * 很明显是二分图匹配,对于候选人来说,有两种情况,被选中和不被选中 * 举个例子,假设一个人选择的是(+3, +1),那么将可以将它分为两组(-1, +3) 和 (-3, +1); * 当选3时-1也要选,当选1时,-3也要选, * 假设又有一个人选了(+3, -2) 那么将会变成(-3, -2) ,(2, -3) 再和上面的集合融合 * 即为(-3, -2, 1) , (-1, +3) , (2, -3)有点不太好理解,还是看代码吧…… */ #include <iostream> #include <cstdio> #include <cstring> #include <vector> using namespace std; vector<int> f[16010], tmp; bool flag[20010]; int m; void add(int a, int b){ //这里a ^ 1 相当于是+ 和 - 的交换,比如3^1 == 2, 2 ^ 1 == 3 //2其实是-1, 3其实是1 f[a ^ 1].push_back(b); f[b ^ 1].push_back(a); } bool dfs(int k){ if(flag[k]) return true; if(flag[k ^ 1]) return false; flag[k] = true; tmp.push_back(k); for(int i = 0; i < f[k].size(); ++i) if( !dfs( f[k][i] ) ) return false; return true; } bool two_SAT(){ for(int i = 1; i <= m; ++i){ if( !flag[2 * i] && !flag[2 * i + 1]){ tmp.clear(); if( !dfs(2 * i) ){ for(int j = 0; j < tmp.size(); ++j) flag[ tmp[j] ] = false; if( !dfs(2 * i + 1) ) return false; } } } return true; } int main(){ int T, n, i, a, b; scanf("%d", &T); for(int cas = 1; cas <= T; ++cas){ printf("Case %d: ", cas); scanf("%d%d", &n, &m); for(i = 1; i <= m; ++i){ flag[2 * i] = flag[2 * i + 1] = false; f[2 * i].clear(); f[2 * i + 1].clear(); } for(i = 0; i < n; ++i){ scanf("%d%d", &a, &b); //这里是把+,-融合到一块了,比如-1=>2, +1=>3, -2=>4, +2=>5依此类推 a = a > 0 ? 2 * a + 1: 2 * (-a); b = b > 0 ? 2 * b + 1: 2 * (-b); add(a, b); } if( two_SAT() ){ tmp.clear(); puts("Yes"); for(i = 1; i <= m; ++i) if( flag[2 * i + 1] ) tmp.push_back(i); if(tmp.size() == 0) puts("0"); else{ printf("%d ", tmp.size()); for(i = 0; i < tmp.size() - 1; ++i) printf("%d ", tmp[i]); printf("%d ", tmp[i]); } } else puts("No"); } return 0; }