算法(C++)金币阵列问题

题目描述

有m × n(m ≤ 100 ,n ≤ 100) 个金币在桌面上排成一个m 行n 列的金币阵列。每一枚金币或正面朝上或背面朝上。用数字表示金币状态,0 表示金币正面朝上,1 表示背面朝上。
金币阵列游戏的规则是:
(1)每次可将任一行金币翻过来放在原来的位置上;
(2)每次可任选2 列,交换这2 列金币的位置。

编程任务

给定金币阵列的初始状态和目标状态,编程计算按金币游戏规则,将金币阵列从初始状态变换到目标状态所需的最少变换次数。

数据输入:
输入有多组数据。第1 行有1 个正整数k,表示有k 组数据。每组数据的第1 行有2 个正整数m 和n。以下的m 行是金币阵列的初始状态,每行有n 个数字表示该行金币的状态,0 表示金币正面朝上,1 表示背面朝上。接着的m 行是金币阵列的目标状态。

结果输出:
将计算出的最少变换次数按照输入数据的次序输出。相应数据无解时输出-1。

输入示例
2
4 3
1 0 1
0 0 0
1 1 0
1 0 1
1 0 1
1 1 1
0 1 1
1 0 1
4 3
1 0 1
0 0 0
1 0 0
1 1 1
1 1 0
1 1 1
0 1 1
1 0 1
输出示例
2
-1

分析

  • 首先考虑行的反转。将初始状态存在二维数组start[][]中,目标数组设为goal[][],若start的第i行中0的个数与goal的第i行中1的个数相等,该行翻转后的结果才可能有意义,则该行进行翻转操作。
  • 所有行都判断完后考虑列的交换。从goal的第1列开始,在start中找一列和该列 i 相同的列 j ,且 j 必须大于 i ,将 i 和 j 交换。
  • 所有操作进行完后判断start和goal是否相同,若相同,返回操作次数,不相同则返回-1.
  • 代码
#include <iostream>

using namespace std;

int start[100][100] = {0};
int goal[100][100] = {0};

int judge(int m,int n){//判断start是否变为goal
    int sum = 0;
    int num = 0;
    for(int i = 0;i<m;i++){
        for(int j = 0;j<n;j++){
            if(start[i][j] == goal[i][j])
                sum++;
        }
    }
    if(sum == m*n)
        num = 1;
    return num;
}
int findstart(int m,int n,int num){//找与goal第num列相同的列
    int sum = 0;
    int goalnum = 0;
    for(int i = 0;i<n;i++){
        for(int j = 0;j<m;j++){
            if(goal[j][num] == start[j][i])
                sum++;
        }
        if(sum == m){
            goalnum = i;
            break;
        }
        sum = 0;
    }
    return goalnum;
}
int num_0(int n,int num){//判断某一行start0和goal1的个数是否相同
    int num0 = 0,num1 = 0,nnum = 0;
    int sum = 0;
    for(int i = 0;i<n;i++){
        if(start[num][i] == 0){
            num0++;
        }
        if(goal[num][i] == 1){
            num1++;
        }
        if(start[num][i] == goal[num][i]){
            nnum++;
        }
    }
    if(nnum == n){
        sum = 2;
    }
    else{
        if(num0 == num1){
            sum = 1;
        }
        else sum = 0;
    }
    return sum;
}
void turnover(int n,int num){//将一行反转
    for(int i = 0;i<n;i++){
        if(start[num][i] == 1){
            start[num][i] = 0;
        }
        else{
            start[num][i] = 1;
        }
    }
}
void changetwo(int m,int num1,int num2){//交换两列
    int t;
    for(int i = 0;i<m;i++){
        t = start[i][num1];
        start[i][num1] = start[i][num2];
        start[i][num2] = t;
    }
}
int change(){
    int m,n;
    cin>>m>>n;
    int sum = 0;
    for(int i = 0;i<m;i++){
        for(int j = 0;j<n;j++){
            cin>>start[i][j];
        }
    }
    for(int i = 0;i<m;i++){
        for(int j = 0;j<n;j++){
            cin>>goal[i][j];
        }
    }
    for(int i = 0;i<m;i++){//若start0=goal1,反转
        if(num_0(n,i) == 1){
            turnover(n,i);
            sum++;
        }
    }
    //判断start的第i列是否与goal的相同,若不同,在start中寻找相同的列,交换start中的2列
    for(int i = 0;i<n;i++){
        int j = findstart(m,n,i);
        if(j>i){
            changetwo(m,i,j);
            sum++;
        }
    }
    if(judge(m,n) == 0){
        sum = -1;
    }
    return sum;
}
int main()
{
    int num;
    cin>>num;
    int answer[100];
    for(int i = 0;i<num;i++){
        answer[i] = change();
    }
    for(int i = 0;i<num;i++){
        cout<<answer[i]<<endl;
    }

    return 0;
}

  • 15
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值