poj1681 高斯消元

題目大意:
一個n*n 的方格 ,我們對它進行染色,每個格子都 可以 染成 白色和黃色,( 一旦我們對這個格子染色 ,他的上下左右 都將改變顏色);給定一個初始狀態 , 求將 所有的 格子 染成黃色 最少需要染幾次? 若 不能 染成 輸出 “inf”
解題思路:
我們可以首先來構造一個矩陣,這個矩陣式幹啥的呢,我們可以認爲這個矩陣是按下一個格子之後它能夠作用的範圍,將能夠作用的範圍用1表示否則用0表示。來舉個例子
就 根據一個3*3的矩陣,對於第一行第一列的元素來說,它可以影響的範圍是它自己 還有它的右面的值和它的下面的值,矩陣中其餘的值都是0,也就是說
A(0,0)=
1 1 0
1 0 0
0 0 0
A(0,1)=
1 1 1
1 0 0
0 0 0
現在我們設一個L矩陣,表示初始的狀態,如果是y就是0,否則就是1,也就是說我們需要將當前的矩陣操爲全是0的矩陣
L + x(1,1)*A(1,1) + x(1,2)*A(1,2) + x(1,3)*A(1,3) + x(2,1)*A(2,1) + … + x(3,3)*A(3,3) = 0 (1)
上述 方程的 x 表示的是一個未知數,因爲我們不知道是否是按下這個按鈕。那麼x(i, j)=0表示不按,否則表示按。那麼我們現在就是求一個這樣的方程解最小的x,那麼上述方程中的矩陣A可以用一個比較大 的矩陣n*n的來表示,然後就是轉換一下關係就行了(具體在代碼中有體現)。那麼現在就是n個未知數,n個方程,在(1)中,可以兩邊加上L,那麼就是變成了:
x(1,1)*A(1,1) + x(1,2)*A(1,2) + x(1,3)*A(1,3) + x(2,1)*A(2,1) + … + x(3,3)*A(3,3) = L(2)
X * A = L(類似這樣的)
然後我們在枚舉一下所有的狀態:我們首先要枚舉的是自由變元的個數,我們對它的所有狀態都進行枚舉,然後得到了如果符合狀態的話就進行自由變元的賦值,然後我們在對可以確定的變元進行操作,那麼肯定就是高斯中的回代過程,然後在進行判斷,我們需要的就是最少的1,也就是最少能夠操作的數。
具體還得詳見代碼:

n*n*n*n的矩陣
將網格編號,從左到右從上到下1到n,第i行表示每個開關對第i個開關影響多少,本題中每個開關最多操作一次,用的是異或版高斯消元

//<span style="font-size:18px;">
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
using namespace std;
const int MAXN = 3e2+5;
const int INF = 1e9+5;
int equ, var;///equ個方程 var個變量
int a[MAXN][MAXN];///增廣矩陣
int x[MAXN];///解的數目
bool free_x[MAXN];///判斷是不是自由變元
int free_xx[MAXN];
int free_num;///自由變元的個數
inline int GCD(int m, int n)
{
    if(n == 0)
        return m;
    return GCD(n, m%n);
}
inline int LCM(int a, int b)
{
    return a/GCD(a,b)*b;
}
int n;
int Gauss()
{
    int Max_r;///當前列絕對值最大的存在的行
    ///col:處理當前的列
    int row = 0;
    int cnt = 0;///自由變元的編號
    for(int col=0; row<equ&&col<var; row++,col++)
    {
        Max_r = row;
        for(int i=row+1; i<equ; i++)///找當前列中最大的行
            if(abs(a[i][col]) > abs(a[Max_r][col]))
                Max_r = i;
        ///交換Max_r行 與 當前行
        if(Max_r != row)
            for(int i=0; i<var+1; i++)
                swap(a[row][i], a[Max_r][i]);

        if(a[row][col] == 0)
        {
            row--;
            free_xx[cnt++] = col;///後面的
            continue;
        }
        for(int i=row+1; i<equ; i++)
        {
            if(a[i][col])
            {
                for(int j=col; j<var+1; j++)
                    a[i][j] ^= a[row][j];
            }
        }
    } // row的值就是 rank 
    for(int i=row; i<equ; i++)
        if(a[i][var])
            return -1;///無解
    return var - row;///自由變元的個數
}
int Solve(int S)
{
    int s = (1<<S);///所有的狀態
    int ans = INF;
    for(int i=0; i<s; i++)///枚舉狀態
    {
        int cnt = 0;
        memset(x, 0, sizeof(x));
        for(int j=0; j<S; j++)
        {
            if(i & (1<<j))
            {
                cnt++;///1的個數,也就是能夠操作的個數
                x[free_xx[j]] = 1;//想象一個矩陣乘一個一維列向量,對應的是列向量的值 
            }
        }
        for(int j=var-S-1; j>=0; j--)
        {
            int tmp = a[j][var], tp, ok = 1;
            for(int k=j; k<var; k++)
            {
                if(a[j][k] && ok) //得找到該行第一個非0值,作爲當前x的答案 
                {
                    tp = k;
                    ok = 0;
                }
                if(a[j][k] && k!=j)
                    tmp ^= x[k];
            }
            x[tp] = tmp;
            cnt += x[tp];
        }
        ans = min(ans, cnt);///最少的操作數
    }
    return ans;
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n;
        equ = var = n*n;
        memset(a, 0, sizeof(a));//將網格編號,從左到右從上到下1到n,每一行表示第i個開關影響的是哪些個開關 
        for(int i=0; i<var; i++)
        {
            int ta = i%n, tb = i/n;
            a[i][i] = 1;
            if(ta > 0)
                a[i][i-1] = 1;
            if(ta < n-1)
                a[i][i+1] = 1;
            if(tb > 0)
                a[i][i-n] = 1;
            if(tb < n-1)
                a[i][i+n] = 1;//第i+n個開關對第i個開關影響1
        }
       /* for(int i = 0 ; i < var;i ++){
            for(int j = 0 ; j < var; j ++){
                cout << a[i][j] << " ";
            }
            cout<< endl;
        }*/ 
        //增廣矩陣的最後一列,表示每個開關的狀態 
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++)
            {
                char ch;
                cin>>ch;
                if(ch == 'y')
                    a[i*n+j][var] = 0;
                else
                    a[i*n+j][var] = 1;
            }
        }
        int S = Gauss();
        if(S == -1)
            puts("inf");
        else
            cout<<Solve(S)<<endl;
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章