題目鏈接
http://acm.hdu.edu.cn/showproblem.php?pid=4827
題意
有一個n個點m條邊的圖,你要給每個點一個0或1的標號,使得每個點與偶數個相同標號的點之間有邊。如果有多解輸出任意一組。
題目保證一定有解。
思路
其實是sjt提醒我這題是高斯消元我纔想到思路的,而且想了很久,自己覺得詢問方案數,以及代數系統是模2的剩餘系的題目,可能和高斯消元還是有一點關係把(比如比較經典的UVa 11542 HDU 5833)
如何建立方程是個難題,首先建立的方程組一定是
或者說是這樣的增廣矩陣
不妨考慮以下幾個要素:
1. i行i列的取值是什麼?也就是一個點建立出的方程要不要考慮它自己的取值?
2. 增廣矩陣最後一列,也就是常數項的那一列取值應該是什麼?
一個點列出的方程肯定和它周圍節點相關的,枚舉一個點在答案中的取值,於是可以列出下表:
答案中自身取值 | 是否考慮自己的權值 | 相鄰點數 | 常數項取值 |
---|---|---|---|
0 | 考慮 | 偶數 | 0 |
0 | 考慮 | 奇數 | 1 |
0 | 不考慮 | 偶數 | 0 |
0 | 不考慮 | 奇數 | 1 |
1 | 考慮 | 偶數 | 1 |
1 | 考慮 | 奇數 | 1 |
1 | 不考慮 | 偶數 | 0 |
1 | 不考慮 | 奇數 | 0 |
接下來希望講清楚自己的思路QAQ,根據上面列出的表格,可以發現:
- 當一個點周圍有奇數個點的時候,把它的係數置爲1,那它的常數項必爲1
- 當一個點周圍有偶數個點的時候,把它的係數置爲0,那它的常數項必爲0
所以就可以通過高斯消元求解了(霧
這裏不用bitset優化就會TLE,所以自己參照大白和網上的一個題解寫了一個模板
代碼
此代碼稍作修改後就可以用來求解HDU 5833
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <bitset>
using namespace std;
typedef pair<int, int> P;
const int MAXN = 1e3 + 5;
int n, m;
bitset<MAXN> A[MAXN];
int x[MAXN];
// 建立線性方程組
// 建立出的方程組每行每列的第一個下標都是0
void setUpEquations(bitset<MAXN> A[MAXN]) {
// 清空原方程組
for (int i = 0; i < n; ++i) A[i].reset();
// 建立方程組, 具體問題具體分析
int u, v;
for (int i = 0; i < m; ++i) {
scanf("%d%d", &u, &v);
--u; --v;
A[u].set(v); A[v].set(u);
}
for (int i = 0; i < n; ++i) if (A[i].count() & 1) {
A[i].set(i);
// 因爲bitset的常數較小,爲省方便
// 直接把增廣矩陣的最後一列放在bitset的最後一列
A[i].set(MAXN - 1);
}
}
// 高斯-約當消元法
// m個方程, n個變量
void gauss(bitset<MAXN> A[MAXN], int *x, int m, int n) {
// 第一步,化爲對角陣
int i = 0, j = 0, k, r, u;
while (i < m && j < n) {
r = i;
for (k = i; k < m; ++k) if (A[k][j]) {
r = k;
break;
}
if (A[r][j]) {
if (r != i) swap(A[r], A[i]);
for (u = i + 1; u < m; ++u) if (A[u][j]) A[u] ^= A[i];
++i;
}
++j;
}
int Rank = i;
// 矩陣已經化簡爲對角陣,如果只需要求出矩陣的秩
// (用於確定方程組有幾組不同的解,如UVa 11542 HDU 5833)
// 直接 return Rank; 即可
// 第二步,回代變量,求解方程組
// 本題中默認自由變量值爲0
for (i = 0; i < n; ++i) x[i] = 0;
for (i = Rank - 1; i >= 0; --i) {
if (A[i][MAXN - 1]) {
u = 0;
while (!A[i][u]) ++u;
x[u] = 1;
for (j = i - 1; j >= 0; --j) if (A[j][u]) {
A[j][u] = 0;
A[j].flip(MAXN - 1);
}
}
}
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
int T, kase = 0;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
setUpEquations(A);
gauss(A, x, n, n);
printf("Case #%d:\n", ++kase);
for (int i = 0; i < n; ++i) putchar('0' + x[i]);
putchar(10);
}
}