hdu5755Gambler Bo+高斯消元

本文介绍了一个矩阵游戏问题的解决方法,玩家需要通过特定的操作使初始矩阵的所有元素变为0。利用高斯消元法建立并求解方程组,找到了一种有效的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

装载请注明出处:http://blog.youkuaiyun.com/xtulollipop
Problem Description
Gambler Bo is very proficient in a matrix game.

You have a N×M matrix, every cell has a value in {0,1,2}.

In this game, you can choose a cell in the matrix, plus 2 to this cell, and plus 1 to all the adjacent cells.

for example, you choose the cell (x,y), the value of (x,y) will be plused 2, and the value of (x−1,y)(x+1,y)(x,y−1)(x,y+1) will be plused 1.

if you choose the cell (1,2), the cell (1,2) will be plused 2, and the cell (2,2)(1,1)(1,3) will be plused 1, the cell (0,2) won’t be changed because it’s out of the matrix.

If the values of some cells is exceed 2, then these values will be modulo 3.

Gambler Bo gives you such a matrix, your task is making all value of this matrix to 0 by doing above operations no more than 2NM times.

Input
First line, an integer T. There are T test cases.

In each test, first line is two integers N,M, and following N lines describe the matrix of this test case.

T≤10,1≤N,M≤30, the matrix is random and guarantee that there is at least one operation solution.

Output
For each test, first line contains an integer num(0≤num≤2NM) describing the operation times.

Following num lines, each line contains two integers x,y(1≤x≤N,1≤y≤M) describing the operation cell.

The answer may not be unique, you can output any one.

Sample Input

2
2 3
2 1 2
0 2 0
3 3
1 0 1
0 1 0
1 0 1

Sample Output

1
1 2
5
1 1
1 3
2 2
3 1
3 3

Author
绍兴一中

Source
2016 Multi-University Training Contest 3

题意:给一个n*m的矩阵,每次操作可以将i,j位加2,它的周围的上下左右位置分别加1,大于2就取膜3,问进行多少次操作可以上举证变成全0状态。分别输出操作步骤。如果结果不唯一,输出任意步骤。
解法:对于每个位置变化的次数我们可以离散化,将他视为一个未知量。针对这个变量我们可以建立一个方程组。这样我们可以建立n*m个方程组,然后高斯消元,解除满足所有方程的一个通解。即该位置上的操作次数。
下边给出了第一个样例的矩阵:
2 1 0 1 0 0 1
1 2 1 0 1 0 2
0 1 2 0 0 1 1
1 0 0 2 1 0 0
0 1 0 1 2 1 1
0 0 1 0 1 2 0

第二个样例的矩阵:
2 1 0 1 0 0 0 0 0 2
1 2 1 0 1 0 0 0 0 0
0 1 2 0 0 1 0 0 0 2
1 0 0 2 1 0 1 0 0 0
0 1 0 1 2 1 0 1 0 2
0 0 1 0 1 2 0 0 1 0
0 0 0 1 0 0 2 1 0 2
0 0 0 0 1 0 1 2 1 0
0 0 0 0 0 1 0 1 2 2

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<math.h>
using namespace std;

/**感谢kuangbin菊苣的模板(博客里抠出来的)**/
const int MAXN=905;
const int mod=3;

int a[MAXN][MAXN];//增广矩阵
int x[MAXN];//解集
bool free_x[MAXN];//标记是否是不确定的变元

inline int gcd(int a,int b){
    int t;
    while(b!=0){
        t=b;
        b=a%b;
        a=t;
    }
    return a;
}
inline int lcm(int a,int b){
    return a/gcd(a,b)*b;//先除后乘防溢出
}

// 高斯消元法解方程组(Gauss-Jordan elimination).(-2表示有浮点数解,但无整数解,
//-1表示无解,0表示唯一解,大于0表示无穷解,并返回自由变元的个数)

//有equ个方程,var个变元。增广矩阵行数为equ,分别为0到equ-1,列数为var+1,分别为0到var.
int Gauss(int equ,int var)
{
    int i,j,k;
    int max_r;// 当前这列绝对值最大的行.
    int col;//当前处理的列
    int ta,tb;
    int LCM;
    int temp;
    int free_x_num;
    int free_index;

    for(int i=0;i<=var;i++)
    {
        x[i]=0;
        free_x[i]=true;
    }

    //转换为阶梯阵.
    col=0; // 当前处理的列
    for(k = 0;k < equ && col < var;k++,col++)
    {
        max_r=k;
        for(i=k+1;i<equ;i++){
            if(abs(a[i][col])>abs(a[max_r][col])) max_r=i;
        }
        if(max_r!=k){
            for(j=k;j<var+1;j++) swap(a[k][j],a[max_r][j]);
        }
        if(a[k][col]==0){
            k--;
            continue;
        }
        for(i=k+1;i<equ;i++){
            if(a[i][col]!=0) {
                LCM = lcm(abs(a[i][col]),abs(a[k][col]));
                ta = LCM/abs(a[i][col]);
                tb = LCM/abs(a[k][col]);
                if(a[i][col]*a[k][col]<0)tb=-tb;//异号的情况是相加
                for(j=col;j<var+1;j++){
                    a[i][j] = ((a[i][j]*ta-a[k][j]*tb)%mod+mod)%mod;  //qmhj
                }
            }
        }
    }

    for (i = k; i < equ; i++){
        if (a[i][col] != 0) return -1;
    }
    if (k < var){
        for (i = k - 1; i >= 0; i--){
            free_x_num = 0;
            for (j = 0; j < var; j++){
                if (a[i][j] != 0 && free_x[j]) free_x_num++, free_index = j;
            }
            if (free_x_num > 1) continue;
            temp = a[i][var];
            for (j = 0; j < var; j++){
                if (a[i][j] != 0 && j != free_index) temp -= a[i][j] * x[j]%mod;
                temp=(temp%mod+mod)%mod;    //qmhj
            }
            x[free_index] = (temp / a[i][free_index])%mod; //qmhj
            free_x[free_index] = 0;
        }
        return var - k;
    }
    for (i = var - 1; i >= 0; i--){
        temp = a[i][var];
        for (j = i + 1; j < var; j++){
            if (a[i][j] != 0) temp -= a[i][j] * x[j];
            temp=(temp%mod+mod)%mod;
        }
        while (temp % a[i][i] != 0) temp+=3;
       // if (temp % a[i][i] != 0) return -2; // 说明有浮点数解,但无整数解.
        x[i] = (temp / a[i][i])%mod;
    }
    return 0;
}
void Gaussinit(){
    memset(a,0,sizeof(a));
    memset(x,0,sizeof(x));
    memset(free_x,0,sizeof(free_x));
}
/************/
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d %d",&n,&m);
        Gaussinit();
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                int x;
                scanf("%d",&x);
                a[i*m+j][i*m+j]=2;
                if(i>0) a[i*m+j][(i-1)*m+j]=1;
                if(i<n-1) a[i*m+j][(i+1)*m+j]=1;
                if(j>0) a[i*m+j][i*m+j-1]=1;
                if(j<m-1) a[i*m+j][i*m+j+1]=1;
                a[i*m+j][n*m]=(mod-x)%3;
            }
        }

        int free = Gauss(n*m,n*m);  //有多解时
        if(free>0){
            for(int i = 0;i < free;i++){
                for(int j = 0;j < n*m;j++){
                    if(j == n*m-free+i) a[n*m-free+i][j] = 1;

                }
            }
            int aa = Gauss(n*m,n*m);
        }

        int ans=0;
        for(int i=0;i<n*m;i++) ans+=x[i];
        printf("%d\n",ans);

        for(int i=0;i<n*m;i++){
            while(x[i]!=0){
                int tempx=i/m+1;
                int tempy=i%m+1;
                printf("%d %d\n",tempx,tempy);
                x[i]--;
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值