洛谷P1275 魔板

P1275 魔板

题目描述

有这样一种魔板:它是一个长方形的面板,被划分成n行m列的n*m个方格。每个方格内有一个小灯泡,灯泡的状态有两种(亮或暗)。我们可以通过若干操作使魔板从一个状态改变为另一个状态。操作的方式有两种:

(1)任选一行,改变该行中所有灯泡的状态,即亮的变暗、暗的变亮;

(2)任选两列,交换其位置。

当然并不是任意的两种状态都可以通过若干操作来实现互相转化的。

你的任务就是根据给定两个魔板状态,判断两个状态能否互相转化。

输入输出格式

输入格式:

 

文件中包含多组数据。第一行一个整数k,表示有k组数据。

每组数据的第一行两个整数n和m。(0<n,m≤100)

以下的n行描述第一个魔板。每行有m个数字(0或1),中间用空格分隔。若第x行的第y个数字为0,则表示魔板的第x行y列的灯泡为“亮”;否则为“暗”。

然后的n行描述第二个魔板。数据格式同上。

任意两组数据间没有空行。

 

输出格式:

 

共k行,依次描述每一组数据的结果。

若两个魔板可以相互转化,则输出YES,否则输出NO。(注意:请使用大写字母)

 

输入输出样例

输入样例#1:
2
3 4
0 1 0 1
1 0 0 1
0 0 0 0
0 1 0 1
1 1 0 0
0 0 0 0
2 2
0 0
0 1
1 1
1 1
输出样例#1:
YES
NO
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<queue>
#define maxn 110
#define mod 10000003
using namespace std;
map<string,bool>vis;
int n,m,T,a[10001];
string t;
string s,cur,nxt;
queue<string>q;
string hash(int x[]){
    string res="";
    for(int i=1;i<=n*m;i++)res+=x[i]+'0';
    return res;
}
bool bfs(){
    while(!q.empty()){
        cur=q.front();q.pop();
        for(int i=1;i<=n;i++){//枚举修改每一行 
            int w=(i-1)*m;
            nxt=cur;
            for(int j=0;j<m;j++){
                if(nxt[w+j]=='0')nxt[w+j]='1';
                else nxt[w+j]='0';
            }
            if(nxt==t)return 1;
            if(!vis[nxt]){
                q.push(nxt);
                vis[nxt]=1;
            }
        }
        for(int i=0;i<m;i++){//枚举修改每一列 
            nxt=cur;
            for(int j=0;j<m;j++){
                if(nxt[i+j*m]=='0')nxt[i+j*m]='1';
                else nxt[i+j*m]='0';
            }
            if(nxt==t)return 1;
            if(!vis[nxt]){
                q.push(nxt);
                vis[nxt]=1;
            }
        }
    }
    return 0;
}
int main(){
    scanf("%d",&T);
    while(T--){
        while(!q.empty())q.pop();
        s="";t="";
        //memset(vis,0,sizeof(vis));
        vis.clear();
        scanf("%d%d",&n,&m);
        for(int i=0;i<n*m;i++){
            scanf("%d",&a[i]);
            s+=a[i]+'0';
        }
        for(int i=0;i<n*m;i++){
            scanf("%d",&a[i]);
            t+=a[i]+'0';
        }
        vis[s]=1;
        q.push(s);
        if(bfs())printf("YES\n");
        else printf("NO\n");
    }
}
0分 手模不出样例,我自己写的宽搜也没过
/*
    第一步:在最外层循环枚举初始的每一列当做目标状态的第一列
    第二步:在每层循环中比较当前这列和目标状态的第一列的同行的数,如果不相同则把初始的那一行翻转(前面先记录,后面记得还原)
    第三步:看看剩下的列是否可以一一对应,如果可以就yes,不可以就继续枚举。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 110
int n,m,T,s[maxn][maxn],t[maxn][maxn];
bool change_x[maxn],vis_y[maxn];
bool check(int x){
    memset(change_x,0,sizeof(change_x));
    memset(vis_y,0,sizeof(vis_y));
    for(int i=1;i<=n;i++)
        if(s[i][x]!=t[i][1])
            change_x[i]=1;//第i行需要翻转 
    vis_y[x]=1;
    for(int i=2;i<=m;i++){//目标状态的第i列 
        bool flag=0;
        for(int j=1;j<=m;j++){//初始状态的第j列 
            if(vis_y[j])continue;
            int cnt=0;
            for(int k=1;k<=n;k++){
                if((change_x[k]&&s[k][j]!=t[k][i])||(!change_x[k]&&s[k][j]==t[k][i]))cnt++;
                else break;
            }
            if(cnt==n){
                vis_y[j]=1,flag=1;
                break;
            }
        }
        if(!flag)return 0;
    }
    return 1;
}
int main(){
    freopen("Cola.txt","r",stdin);
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&s[i][j]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&t[i][j]);
        bool flag=0;
        for(int i=1;i<=m;i++){//枚举初始状态的每一列与目标状态对应 
            int now=check(i);
            if(now==1){
                printf("YES\n");
                flag=1;break;
            }
        }
        if(!flag)printf("NO\n");
    }
}
100分

 

转载于:https://www.cnblogs.com/thmyl/p/7593307.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值