题目描述
有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;
}