金币阵列问题

问题描述

在这里插入图片描述

算法思想与步骤

  1. 如果可以实现初始矩阵转化为目标矩阵,那么初始矩阵中一定存在一列是目标矩阵中的第一列通过 列变换+行翻转 而来的;为了能够得到最小交换次数,则需要把所有列依次作为第一列进行遍历,然后进行下一步操作
  2. 第一列已经归位,所以目前需要做的是:让其他所有列都能变为目标矩阵中的对应列。则从目标矩阵第二列开始遍历,设遍历的为第i列。
  3. 每次遍历过程中都需要遍历初始矩阵的列(从目标矩阵当前的i列开始遍历,之前列的已经与目标矩阵一致)。如果没有找到与第i列相同的列,则没有找到,跳出该步,返回第一步。如果找到,设该列为第k列,则需要进行交换操作(因为可能存在不止一列相同的情况,所以交换时要考虑以下情况)。
    • 如果k==i,可以直接遍历下一列
    • 如果k!=i,考虑交换的后续影响
      • 第i列初始矩阵与目标矩阵第k列一致,且初始矩阵第k列与目标矩阵第i列相等
      • 第i列初始矩阵与目标矩阵第k列一致,但初始矩阵第k列与目标矩阵第i列不相等
  4. 显然我们为了得到最少交换次数,我们应该选择第1种情况,如果其存在,直接交换然后跳出本次列遍历即可,如果第一种情况不存在,再选择第二种情况。(贪心算法,用两个标志实现,代码中的flag与found)
  5. 用一个数据来存储当前最小次数,如果执行完了第二步,需要把结果与当前最小次数进行比较,更新最小值。

复杂度分析

  • 时间复杂度
    分析一个金币阵列,有m行n列。
    第1步:提供了一个大循环,需要n次,需要时间O(n)。翻转所有的行需要时间O(m)。
    第2步:需要遍历后序每一列,需要时间O(n)
    第3步:需要找到匹配的每一列,每次判断需要O(m),共n次判断,并且有n列,总复杂度O(mn²)
    第5步:为O(1)
    总时间复杂度为O(n
    (m+(mn²)))=O(mn+mn³) = O(mn³)
  • 空间复杂度
    存在中间矩阵,复杂空间复杂度为O(m*n)

疑问剖析

1、这个算法是否遍历了所有交换情况?
通过算法步骤可以发现,我们只是把可能无序的变换(且不会有循环的变换),转化为了有序的变换。由于题目最小次数的要求,这样求得到的一定是最小的。

2、如果不考虑交换后两列是否对等,会出现哪种情况
按照以下样例进行就会发现错误
初始矩阵:1 1 1 1 0 0 0 0
目的矩阵:0 1 0 1 0 1 0 1
明显交换第6列与第一列时,不用行变换,变为
0 1 1 1 0 1 0 0
0 1 0 1 0 1 0 1
进行第2、3步,到第三列时,肉眼可看,与第8列交换即可得到目标矩阵,如果不考虑交换后的结果,1会直接与第5列交换,则会多交换一次,不满足最小情况。
所以需要考虑交换的情况(即第三步的第二层分析)

代码

//复杂度O(m*n^3)
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstdio>
#include<iostream> 
#include<cstring>
#include<vector>
#define size 200
int num; //输入几组数据 
int row; //行数
int column; //列数
int count; //交换次数
int min;
int a[size][size]; //目标矩阵
int b[size][size]; //初始矩阵
int c[size][size]; //临时存放矩阵
int found; //初始到最终是否有交换
void trans_row(int x) // 第x行取反 
{
	int i;
	for (i = 0;i<column; i++) 
		b[x][i] = b[x][i]^1; // 异或取反 
	count++;
}
void trans_column(int x, int y) // 交换x和y列 
{
	int i;
	int temp;
	for(i = 0; i < row; i++)
	{
		temp=b[i][x];
		b[i][x]=b[i][y];
		b[i][y]=temp;
	}
	if (x != y) 
		count++;
}
int is_same(int x, int y) //比较x和y列是否相同 
{
	int i;
	for(i = 0; i <row; i++)
		if (a[i][x] != b[i][y])
			return 0;
	return 1;
}
void copy(int a[size][size], int b[size][size]) // 拷贝数组 
{
	int i,j;
	for (i = 0; i <row; i++)
		for (j = 0; j < column; j++)
			a[i][j] = b[i][j];
}
int main()
{
	int i,j,k,p;
	int exchgmin[size];
	scanf("%d",&num);
	for(i=0;i<num;i++)
	{
		scanf("%d",&row);
		scanf("%d",&column);
		for(j=0;j<row;j++)
			for(k=0;k<column;k++)
				scanf("%d",&a[j][k]);
		for(j=0;j<row;j++)
			for(k=0;k<column;k++)
				scanf("%d",&b[j][k]);
		copy(c,b); //保护初始矩阵b 
		min=row+column+1;
		for(j=0;j<column;j++)
		{
			copy(b,c); //恢复初始数组b 
			count=0;  //交换次数清零 
			trans_column(0,j); //把每一列都假设成为第一列的目标状态,穷举这column中情况 
			for(k=0;k<row;k++)
			{ //如果行不同,则将行转换 
				if(a[k][0]!=b[k][0])
				trans_row(k);
			}
			int lo[200],flag=0,tot=0;
			for(k=1;k<column;k++)
			{//穷举其他所有列,如果相同则交换,说明目标状态统一,否则找不到与该列相同,说明不可行 
				found=0;//记录是否找到相同列 
				tot=0;flag=0;//flag记录是否有交换后都相同的列 
				for(p=k;p<column;p++)
				{
					if(is_same(k,p))
					{
						found=1;
						if(p==k)
							{break;}
						lo[tot++]=p;
						if(is_same(p,k))//此步实现优先选择列一致的 
							{flag=1;trans_column(k,p);break;}
					}
				}
				if(!found)
					break;
				else if(!flag && tot)//没有交换后两列都符合的,则与第一次发现的列交换 
					trans_column(k,lo[0]);
			}
			if(found&&count<min) //如果可行,找出最小值 
				min=count; 
		}
		if(min<row+column+1) //如果交换次数比初始值小,将其保存为当前组的最小交换次数,否则不可实现交换 
			exchgmin[i]=min;
		else exchgmin[i]=-1;
	}
	for(i=0;i<num;i++)
	printf("%d\n",exchgmin[i]);
	return 0;
}
/*
1
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
*/
  • 7
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr.Toser

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值