首页 > 代码库 > POJ 1830 开关问题 高斯消元,自由变量个数

POJ 1830 开关问题 高斯消元,自由变量个数

http://poj.org/problem?id=1830

如果开关s1操作一次,则会有s1(记住自己也会变)、和s1连接的开关都会做一次操作。

那么设矩阵a[i][j]表示按下了开关j,开关i会被操作一次,记得a[i][i] = 1是必须的,因为开关i操作一次,本身肯定会变化一次。

所以有n个开关,就有n条方程,

每个开关的操作次数总和是:a[i][1] + a[i][2] + ... + a[i][n]

那么sum % 2就代表它的状态,需要和(en[i] - be[i] + 2) % 2相等。就是操作次数的奇偶性要相等。

那么来一个高斯消元,就知道是否有可能有解。

在有解的情况下,可能存在自由变量,什么意思呢?

就是假设有k个自由变量,那么就是前n - k个变量有固定的操作,使得能变成最终结果,那么这k个自由变量就可以任取值了,一共有2^k种不同的取值方案。因为方案不要求有顺序,所以那个灯取那个状态,都是相同的一种解。

技术分享
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;


#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <bitset>
const int maxn = 1e2 + 20;
class GaussMatrix {  //复杂度O(n3)
public:
    int a[maxn][maxn];
    int equ, val; //方程(行)个数,和变量(列)个数,其中第val个是b值,不能取
    void init() {
        for (int i = 1; i <= equ; ++i) {
            for (int j = 1; j <= val; ++j) {
                a[i][j] = 0;
            }
        }
    }
    void swapRow(int rowOne, int rowTwo) {
        for (int i = 1; i <= val; ++i) {
            swap(a[rowOne][i], a[rowTwo][i]);
        }
    }
    void swapCol(int colOne, int colTwo) {
        for (int i = 1; i <= equ; ++i) {
            swap(a[i][colOne], a[i][colTwo]);
        }
    }
//    bool same(double x, double y) {
//        return fabs(x - y) < eps;
//    }
    int guass() {
        int k, col; // col,当前要处理的列, k当前处理的行
        for (k = 1, col = 1; k <= equ && col < val; ++k, ++col) { //col不能取到第val个
            int maxRow = k; //选出列最大值所在的行,这样使得误差最小。(没懂)
            for (int i = k + 1; i <= equ; ++i) {
                if (abs(a[i][col]) > abs(a[maxRow][col])) {
                    maxRow = i;
                }
            }
            if (a[maxRow][col] == 0) { //如果在第k行以后,整一列都是0
                --k; //则这个变量就是一个自由变量。
                continue;
            }
            if (maxRow != k) swapRow(k, maxRow); // k是当前的最大行了
            for (int i = col + 1; i <= val; ++i) { //整一列约去系数
                a[k][i] /= a[k][col];
            }
            a[k][col] = 1; //第一个就要变成1了,然后它下面和上面的变成0
            for (int i = 1; i <= equ; ++i) {
                if (i == k) continue; //当前这行,不操作
                for (int j = col + 1; j <= val; ++j) {
                    a[i][j] = (a[i][j] - a[i][col] * a[k][j] + 2) % 2;
                }
                a[i][col] = 0;
            }
//            debug();
        }
        for (int res = k; res <= equ; ++res) {
            if (a[res][val] != 0) return -1; //方程无解
        }
        return val - k; //自由变量个数
    }
    void debug() {
        for (int i = 1; i <= equ; ++i) {
            for (int j = 1; j <= val; ++j) {
                cout << a[i][j] << " ";
            }
            printf("\n");
        }
        printf("*******************************************\n\n");
    }
}arr;
int be[maxn], en[maxn];
void work() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> be[i];
    for (int i = 1; i <= n; ++i) cin >> en[i];
    arr.init();
//    memset(&arr, 0, sizeof arr);
    arr.equ = n, arr.val = n + 1;
    do {
        int x, y;
        cin >> x >> y;
        if (x == 0 && y == 0) break;
        arr.a[x][x] = 1;
        arr.a[y][x] = 1;
    } while (true);
    for (int i = 1; i <= n; ++i) {
        arr.a[i][n + 1] = (en[i] - be[i] + 2) % 2;
        arr.a[i][i] = 1; //这个是必须的。
    }
//    arr.debug();
    int res = arr.guass();
    if (res == -1) {
        cout << "Oh,it‘s impossible~!!" << endl;
    } else cout << (1 << res) << endl;
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    int t;
    cin >> t;
    while (t--) work();
    return 0;
}
View Code

 

POJ 1830 开关问题 高斯消元,自由变量个数