HDU 5755 Gambler Bo

Gambler Bo

Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 676    Accepted Submission(s): 274
Special Judge


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 (x1,y)(x+1,y)(x,y1)(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.

T10,1N,M30, the matrix is random and guarantee that there is at least one operation solution.
 

 

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

Following num lines, each line contains two integers x,y(1xN,1yM) 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

用高斯消元法可解。每个位置的结果可以由他本身和四个方向上的变化决定。

那么设横坐标为每个位置上的数值(一共n*m个位置),纵坐标为对这个数值的影响。A[i][j]=1表示j使i+1 ,A[i][j]=2表示j使i+2  同理A[i][j]=0表示j使i+0

那么就可以构造一个(n*m) *(n*m)的方阵。设X为(n*m)*1的矩阵表示n*m个位置每个位置增加的次数, B为(n*m)*1的矩阵,B与题目中给的值有关,这里表示增加多少使得终态为0.

AX=B,解出X矩阵就行了..

卧槽 好烦 写的这么乱,不过还是挺好理解的....

/* ***********************************************
Author        :guanjun
Created Time  :2016/7/28 8:36:24
File Name     :hdu5755.cpp
************************************************ */
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <iomanip>
#include <list>
#include <deque>
#include <stack>
#define ull unsigned long long
#define ll long long
#define mod 3
#define INF 0x3f3f3f3f
#define maxn 10010
#define cle(a) memset(a,0,sizeof(a))
const ull inf = 1LL << 61;
const double eps=1e-5;
using namespace std;

int a[1100][1100];
int A[1100][1100];
int x[1100];
int dir[4][2]={1,0,0,1,0,-1,-1,0};
int exgcd(int a,int b,int &x,int &y){
    if(!b){x = 1; y = 0; return a;}
    else{
        int r = exgcd(b,a%b,y,x);
        y -= x * (a/b);
        return r;
    }
}
int lcm(int a,int b){
    int x = 0, y =0;
    return a / exgcd(a,b,x,y) * b;
}
void Gauss(int n,int m){
    int r,c;
    //列主元消去
    for(r=0,c=0;r<n&&c<m;c++){
        int maxr=r;
        for(int i=r+1;i<n;i++)if(abs(A[i][c])>abs(A[maxr][c]))maxr=i;
        if(maxr!=r)for(int i=c;i<=m;i++)swap(A[r][i],A[maxr][i]);
        if(!A[r][c])continue;
        for(int i=r+1;i<n;i++)if(A[i][c]){
            int d=lcm(A[i][c],A[r][c]);
            int t1=d/A[i][c],t2=d/A[r][c];
            for(int j=c;j<=m;j++)
                A[i][j]=((A[i][j]*t1-A[r][j]*t2)%mod+mod)%mod;
        }
        r++;
    }
    for(int i=r;i<n;i++)if(A[i][m])return ;
    //从下到上
    for(int i=r-1;i>=0;i--){
        x[i]=A[i][m];
        for(int j=i+1;j<m;j++)
            x[i]=((x[i]-A[i][j]*x[j])%mod+mod)%mod;
        int x1=0,y1=0;
        //这里是用exgcd求逆元,也可以用费马小定理求,如果mod是素数
        int d = exgcd(A[i][i],mod,x1,y1);
        //cout<<"d "<<d<<endl;
        x1=((x1%mod)+mod)%mod;
        x[i]=x[i]*x1%mod;
    }
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    //freopen("out.txt","w",stdout);
    int t,n,m;
    cin>>t;
    while(t--){
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++)scanf("%d",&a[i][j]);
        }
        cle(A);
        for(int i=0;i<n*m;i++){
            A[i][i]=2;//选择自己 加2
            int r=i/m;int c=i%m;

            for(int j=0;j<4;j++){
                int x=r+dir[j][0];
                int y=c+dir[j][1];
                if(x<n&&x>=0&&y<m&&y>=0)A[i][x*m+y]=1;//四个方向+1
            }
            A[i][n*m]=(3-a[r][c])%3;//每个位置需要加的数  和初始状态有关
        }
        Gauss(n*m,n*m);
        int cnt=0;
        vector<int>v;
        for(int i=0;i<n*m;i++){
            while(x[i]){
                x[i]--;
                v.push_back(i);
            }
        }
        printf("%d\n",v.size());
        for(int i=0;i<v.size();i++){
            int r=v[i]/m;
            int c=v[i]%m;
            r++,c++;
            printf("%d %d\n",r,c);
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/pk28/p/5713509.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值