[hdu4598]二分图判定,差分约束
题意: 给一个图,问能否给每个点分配一个实数值,使得存在一个数实数T,所有点满足:|value(i)| < T 且 u,v之间有边<=> |value(u)-value(v)| >= T。(注意等价符号)
思路:
由性质可得,两相邻点的分配的值的符号相反,于是先对原图做一个二分图判定,如果是非二分图,则无解。对二分图染色后,假设color[i]=1,则表示i点为正值,color[i]=-1,则表示为负。在已知每个点正负值的基础上,绝对值符号可以去掉,差分约束模型便出来了。这里有个细节,由于是实数,在遇到<和<=的时候比较麻烦,幸运的是我们可以用整数来代替实数,比如条件a < b可以看成是 a <= (b - 1),只要整数范围足够大即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | #include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <queue> #include <cmath> #include <algorithm> using namespace std; const int T = 12345; struct Graph { vector<vector< int > > G; void clear() { G.clear(); } void resize( int n) { G.resize(n + 2); } void add( int u, int v) { G[u].push_back(v); } vector< int > & operator [] ( int u) { return G[u]; } }; Graph G, E; int n; bool mark[345][345]; int vis[345]; void add( int u, int v, int w) { G.add(v, u); E.add(v, w); } queue< int > Q; int D[345], cnt[345]; bool relax( int u, int v, int w) { if (D[u] > D[v] + w) { D[u] = D[v] + w; return true ; } return false ; } bool SPFA( int s) { while (!Q.empty()) Q.pop(); memset (D, 0x3f, sizeof (D)); memset (cnt, 0, sizeof (cnt)); D[s] = 0; memset (vis, 0, sizeof (vis)); vis[s] = 1; cnt[s] ++; Q.push(s); while (!Q.empty()) { int node = Q.front(); Q.pop(); vis[node] = 0; int sz = G[node].size(); for ( int i = 0; i < sz; i ++) { int u = G[node][i]; if (relax(u, node, E[node][i])) { if (!vis[u]) { vis[u] = 1; cnt[u] ++; if (cnt[u] > n) return false ; Q.push(u); } } } } return true ; } bool Color( int k, int c) { vis[k] = c; for ( int i = 0; i < n; i ++) { if (mark[k][i]) { if (vis[i]) { if (vis[i] == vis[k]) return false ; } else if (!Color(i, -c)) return false ; } } return true ; } bool check() { memset (vis, 0, sizeof (vis)); for ( int i = 0; i < n; i ++) { if (!vis[i]) { if (!Color(i, 1)) return false ; } } G.clear(); E.clear(); G.resize(n); E.resize(n); // 添加一个源点编号为 n, 并从源点引出n条到每个点的有向边,边权为0 for ( int i = 0; i < n; i ++) { add(i, n, 0); } for ( int i = 0; i < n; i ++) { for ( int j = i + 1; j < n; j ++) { if (mark[i][j]) { if (vis[i] > 0) add(j, i, -T); else add(i, j, -T); } else { if (vis[i] > 0) add(i, j, T - 1); else add(j, i, T - 1); } } } n ++; // 多了一个源点 return SPFA(n - 1); } int main() { #ifndef ONLINE_JUDGE freopen ( "in.txt" , "r" , stdin); #endif // ONLINE_JUDGE int T; cin >> T; while (T --) { cin >> n; memset (mark, 0, sizeof (mark)); for ( int i = 0; i < n; i ++) { char s[345]; scanf ( "%s" , s); for ( int j = 0; j < n; j ++) { mark[i][j] = s[j] == '1' ; } } puts (check()? "Yes" : "No" ); } return 0; } |