金币阵列问题

问题描述:有m*n(1 ≤ m, n ≤ 100)个金币在桌面上排成一个 m 行 n 列的阵列。每一枚金币或正面朝上或背面朝上。用数字表示金币状态,0表示金币正面朝上,1 表示背面朝上。

金币阵列游戏的规则是: 
1. 每次可将任一行金币翻过来放在原来的位置上; 
2. 每次可任选 2 列,交换这 2 列金币的位置。 
本题要求对于给定的金币阵列初始状态和目标状态,编程计算按金币游戏规则,将金币阵列从初始状态变换到目标状态所需的最少变换次数。

数据输入:

输入的测试数据的第一行两个正整数 m, n. 接下来是 m 行,每行有 n 个用空白符分隔的 0 或 1. 这 m*n 个 0-1 表示金币的初始状态阵列。最后是 m 行,每行 n 个 用空白符分隔的 0 或 1,表示金币阵列的目标状态。

数据输出:
对于测试用例,输出一行包含一个整数,表示按照要求规则将金币阵列从初始状态变换为目标状态所需要的最少变换次数。如果不能按照变换规则将初始状态变换为目标状态(即无解时)则输出 -1。 

示例:

输入:                输出:                       输入:               输出:

4 3       2                              4 3        -1

1 0 1                  1 0 1

0 0 0                  0 0 0

1 1 0                  1 0 0

1 0 1                  1 1 1

1 0 1                  1 1 0

1 1 1                  1 1 1                   

0 1 1                  0 1 1

1 0 1                  1 0 1

代码:

#include <iostream>
#include <fstream>
#define MAXSIZE 100
using namespace std;
int matrix1[MAXSIZE][MAXSIZE],matrix2[MAXSIZE][MAXSIZE],matrix3[MAXSIZE][MAXSIZE];
void matrixcopy(int ma1[MAXSIZE][MAXSIZE],int ma2[MAXSIZE][MAXSIZE])
{
    for (int i = 0;i<MAXSIZE;i++)
    {
        for (int j = 0;j<MAXSIZE;j++)
        {
            ma2[i][j] = ma1[i][j];
        }
    }
}

void changerows(int x)
{
    for (int i=0;i<MAXSIZE;i++)
    {
        matrix3[x][i]=matrix3[x][i]*(-1)+1;
    }
}

void changecols(int x,int y)
{
    int p;
    for (int i=0;i<MAXSIZE;i++)
    {
        p=matrix3[i][x];
        matrix3[i][x]=matrix3[i][y];
        matrix3[i][y]=p;
    }
}

bool issamecols(int i,int p,int m)
{
    bool same=true;
    for (int k=0;k<m;k++)
    {
        if (matrix3[k][i]!=matrix2[k][p])
        {
            same=false;
            break;
        }
    }
    return same;
}

void main()
{
    int m,n;
    cin>>m>>n;
    for (int i = 0;i < 2*m;i++)
    {
        for (int j = 0;j < n;j++)
        {
            if (i < m)
            {
                cin>>matrix1[i][j];
            }
            else
            {
                cin>>matrix2[i-m][j];
            }
        }
    }

    int best=m+n+1;
    int num=0;
    bool isok=false;

    for (int i=0;i<n;i++)
    {
        num=0;
        isok=false;
        matrixcopy(matrix1,matrix3);
        for (int j=0;j<m;j++)
        {
            if (matrix3[j][i]!=matrix2[j][0])
            {
                changerows(j);
                num++;
            }
        }
        if (i!=0)
        {
            changecols(0,i);
            num++;
        }
        int p;
        int k;
        for (k=1;k<n;k++)
        {
            for (p=1;p<n;p++)
            {
                if (issamecols(p,k,m)==true)
                {
                    isok=true;
                    if (k!=p)
                    {
                        changecols(k,p);
                        num++;
                    }
                    break;

                }
                
            }
            if (isok==false)
            {
                break;
            }
            isok=false;
        }
        if (k==n&&(num<best))
        {
            best=num;            
        }
    }
    if (best==m+n+1)
    {
        best=-1;
    }
    printf("
%d",best);
    system("pause");
}

解题思路:

刚拿到这个问题的时候很棘手,一开始的想法是把所有可变换的情况枚举出来,然后跟目标结果进行比较,事实证明这个方式时间复杂度较高。

后面想到用列来决定行或者用行来决定列,也就是例如说是两行是 a1, b1 ,c1和a2,b2,c2用行来决定列的意思就使a1,b1,c1变换之后与a2,b2,c2保持一致,那么在这种情况下肯定有一种是满足的,但这个时候又遇到了一个问题,就此例而言,三个数有8种排列方式,那么其中变换之后必然无解的是初始状态和最终状态一个全部为同一面,另一个其中正反各不同这个判断在有无解上给了我们一定的优势,但是他是这种问题无解的充分非必要条件,因为就算这一列满足了,其他列我们也得满足。那么这个方法的解决方式即为,对于初始情况的每一列,我们先判断能否变换为最终状态的那一列,即是正(初始)=正(目标)或者正(初始)=反(目标)如果可以的话,我们就进行从一行到目标行的转变③判断其他行是否都正确,若否继续回到②,这个方法理论上来说是对的,但是在第二个步骤中很显然是很蛋疼的一件事。

第三就是用列来决定行了,这个相比起行决定列要简单得多,原因即为把某个特定行的初始行转为目标行有许多种情况,因为在对于整个金币矩阵来说行是不能动的,但是列就不一样了,我们把第一列作为基准列,那么我们让初始情况中的每一列经过变换之后与第一列相同,这样的变换只有一种,且此变换中只有行的取反变换,那么对于每一个初始列,我们都进行变换为目标值的第一列,此时我们再看其他列是否能一一找到目标值中对应的列,这样的话就简单多了。以上的代码也是基于此思想。如果有蛋疼的童鞋可以试试第二种想法,虽然有点蠢但是感觉是可以实现的。

原文地址:https://www.cnblogs.com/xds1224/p/3556843.html