HDU - 5755 高斯消元解同余方程

题意:

n*m的矩阵,每个矩阵都有0,1,2三种数值,一开始每个点都有一个初始值,每次可以选择一个位置+2,然后该位置的上下左右都+1,问多少次之后可以将所有位置的数值都变成0。

思路:

很典型的开关问题,没什么好说的,高斯消元,不过这题不是以前的异或方程,而是模3同余方程。

代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1111;
typedef int Matrix[MAXN][MAXN];

// 高斯消元解模k同余方程
struct Gauss {
    int A[MAXN][MAXN];
    bool fre[MAXN];
    int Rank, equ, var, fnum;
    int indx[MAXN], x[MAXN];

    void init(int m, int n) {
        fnum = 0; equ = m; var = n;
        memset(fre, 0, sizeof(fre));
        memset(x, 0, sizeof(x));
        for(int i = 0; i <= m; i++)
            for(int j = 0; j <= n; j++) A[i][j] = 0;
    }


    void get_root(int m, int n, int mod) { // rank of matrix
        int i = 0, j = 0, k, r, u; //i:row j:col
        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) for (k = 0; k <= n; k++) swap(A[r][k], A[i][k]);
                for (u = i + 1; u < m; u++) if (A[u][j]) {
                    int lcm = A[u][j] / __gcd(A[u][j], A[i][j]) * A[i][j];
                    int t1 = lcm / A[u][j], t2 = lcm / A[i][j];
                    for(k = i; k <= n; k++)
                        A[u][k] = (t1 * A[u][k] % mod - t2 * A[i][k] % mod + mod) % mod;
                }
                i++;
            }
            else {
                fre[j] = 1;
                indx[fnum++] = j;
            }
            j ++;
        }
        Rank = i;
        k = var - 1;
        for (int i = Rank - 1; i >= 0 && k >= 0; i--){ //only needs to solve 'rank' num of equation?
            while (k >= 0 && fre[k]) k--;
            if (k >= 0){
                x[k] = A[i][var];
                for (int j = k + 1; j < var; j++)
                    x[k] = (x[k] - A[i][j] * x[j] % mod + mod) % mod;
                x[k] = x[k] * A[k][k] % mod;
                k --;
            }
        }
    }
} gauss;

int mp[MAXN][MAXN];
const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};

int main() {
    int T, n, m;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &m);
        gauss.init(n * m, n * m);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                scanf("%d", &mp[i][j]);
                int t1 = m * i + j;
                gauss.A[t1][t1] = 2;
                gauss.A[t1][n * m] = (3 - mp[i][j]) % 3;
                for (int k = 0; k < 4; k++){
                    int cx = i + dx[k], cy = j + dy[k];
                    if (cx >= 0 && cx < n && cy >= 0 && cy < m){
                        int t2 = m * cx + cy;
                        gauss.A[t2][t1] = 1;
                    }
                }
            }
        }
        gauss.get_root(n * m, n * m, 3);
        int cnt = 0;
        for (int i = 0; i < n * m; i++) cnt += gauss.x[i];
        printf("%d\n", cnt);
        for (int i = 0; i < n * m; i++){
            for (int j = 0; j < gauss.x[i]; j++)
                printf("%d %d\n", i / m + 1, i % m + 1);
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值