hdu5755Gambler Bo

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5755

题意:给定一个只含{0,1,2}的n*m的矩阵。有一种操作:将(x,y)位置+2同时(x-1,y),(x+1,y),(x,y-1),(x,y+1)都会+1。要求进行<=2*n*m次操作将矩阵变为全0,数据保证至少有一组解。

分析:前段时间刚做完一道这样的题,比赛的时候因为卡其他题了这道题却没看到。想多练一下的转E.Harmonious Matrices。。再说下这种题的思路吧,我们可以设第一行每个位置的操作次数为xi,0<=xi<3,然后显然知道了第一行我们就能将整个矩阵都推导出来。如果直接暴力枚举显然是不行的,我们观察发现每填完一行只能确定上一行的元素,那么想要确定n行显然是要推到第n+1行,但是实际情况第n+1行全为0。所以对于第n+1行的m个位置我们能得到m个含m个未知数xi等于0的方程,那么只要解这个方程组就能得到答案x啦。PS:出题人的做法类似但是复杂度上差很多,出题人说的做法是设n*m个变量,然后根据题目的要求建n*m个方程,这样是O(n^3*m^3)的。我的做法是设m个变量,这样就能O(m^3)啦。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<vector>
#include<string>
#include<stdio.h>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=35;
const int mod=100000000;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
const int MAX=2000000010;
const ll INF=1ll<<55;
const double pi=acos(-1.0);
typedef double db;
typedef unsigned long long ull;
int a[N][N],p[N][N],f[N][N][N];
int get(int i,int j,int k) {
    int sum=(f[i][j-1][k]+2*f[i][j][k]+f[i][j+1][k]+f[i-1][j][k])%3;
    return ((3-sum)%3+3)%3;
}
int getans(int i,int j) {
    int sum=(p[i][j-1]+2*p[i][j]+p[i][j+1]+p[i-1][j]+a[i][j])%3;
    return ((3-sum)%3+3)%3;
}
void gauss(int n,int m) {
    int d,i,j,k,h,w=0;
    for (i=1,j=1;j<m;j++,w=0) {
        for (k=i;k<=n;k++)
        if (p[k][j]) w=k;
        if (w) {
            for (k=j;k<=m;k++) swap(p[i][k],p[w][k]);
            for (k=i+1;k<=n;k++)
            if (p[k][j]) {
                d=(p[k][j]*p[i][j]%3+3)%3;
                for (h=j;h<=m;h++) p[k][h]=((p[k][h]-d*p[i][h])%3+3)%3;
            }
            i++;
        }
        if (i>n) break ;
    }
    for (j=1;j<=m;j++) f[1][j][j]=0;
    for (j=i-1;j;j--) {
        for (k=1;k<m;k++)
        if (p[j][k]) break ;
        for (d=0,h=k+1;h<m;h++)
        if (f[1][h][h]&&p[j][h]) d=(d+f[1][h][h]*p[j][h])%3;
        f[1][k][k]=p[j][k]*(3-d+p[j][m])%3;
    }
    memset(p,0,sizeof(p));
    for (j=1;j<=m;j++) p[1][j]=(f[1][j][j]+3)%3;
}
int main()
{
    int i,j,k,n,m,t,ans;
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d", &n, &m);
        memset(a,0,sizeof(a));
        for (i=1;i<=n;i++)
            for (j=1;j<=m;j++) scanf("%d", &a[i][j]);
        memset(f,0,sizeof(f));
        for (i=1;i<=m;i++) f[1][i][i]=1;
        for (i=2;i<=n+1;i++)
            for (j=1;j<=m;j++) {
                f[i][j][m+1]=(3-a[i-1][j]+get(i-1,j,m+1))%3;
                for (k=1;k<=m;k++) f[i][j][k]=get(i-1,j,k);
            }
        memset(p,0,sizeof(p));
        for (i=1;i<=m;i++) {
            p[i][m+1]=((3-f[n+1][i][m+1])%3+3)%3;
            for (j=1;j<=m;j++) p[i][j]=f[n+1][i][j];
        }
        gauss(m,m+1);ans=0;
        for (i=1;i<=m;i++) ans+=p[1][i];
        for (i=2;i<=n;i++)
            for (j=1;j<=m;j++) p[i][j]=getans(i-1,j),ans+=p[i][j];
        printf("%d\n", ans);
        for (i=1;i<=n;i++)
            for (j=1;j<=m;j++)
                for (k=1;k<=p[i][j];k++) printf("%d %d\n", i, j);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值