poj1681 高斯消元

来源:互联网 发布:lolita淘宝 编辑:程序博客网 时间:2024/05/16 10:20

题目大意:
一个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;}
0 0
原创粉丝点击