1. 程式人生 > >計算機演算法設計與分析1-4 金幣陣列問題

計算機演算法設計與分析1-4 金幣陣列問題

問題描述:有m*n枚金幣在桌面上排列成一個m行n列的金幣陣列。每一枚金幣或正面朝上,或背面朝上。用數字表示金幣狀態,0表示正面朝上,1表示背面朝上。

金幣陣列遊戲的規則是:

    (1)每次將任一行金幣翻過來放在原來的位置上。

    (2)每次可以任選2列,交換這2列金幣的位置。

    任務:給定金幣的初始狀態和目標狀態,程式設計計算按金幣遊戲規則,將金幣排列從初始狀態變換到目標狀態所需的最少變換次數。

這題我一開始想著先做一遍行反轉(這一行所有元素在初始和目標矩陣的都不同時),然後再做列交換,然而寫了半天也不知道這個策略怎麼寫,從前往後走一遍的話不能保證最優。

說實話題解的做法我不太理解原理,但大致也覺得這麼是對的。

題解中n+m+1設為最大交換次數,我感覺最大也就是m + n/2,不知道這樣理解對不對。

具體步驟:

1.將初始矩陣所有列依次放到第一列(我的理解是這樣,第一是因為這裡的規則是進行行反轉,列交換,如果是列反轉,行交換的話,就是所有行依次放到第一行了,   第二是後面從前往後檢視是否有列相同或者交換可得到,這在某種意義上進行了列舉,每一種可能都進行了嘗試,以每一列為基準進行嘗試,這與第二步也有關)

2. 對比當前第一列,也就是交換之前的1到n中某一列的元素是不是和目標矩陣一樣,不一樣就進行行反轉

3.從前到後看每一列,如果當前列與目標矩陣中的當前列相同或者可以交換得到,那麼就計算次數並與上次結果比較大小,如果不同且無法交換得到,那麼這種嘗試是失敗的(這裡注意最後比較的時候是從當前列而不是下一列,為了判斷這一列是不是相同)

下面是程式碼

#include<bits/stdc++.h>
using namespace std;
#define maxn 110
int terms,n,m,inti[maxn][maxn],des[maxn][maxn],tep[maxn][maxn];
int cnt=0;
void transfer_column(int col)//tep
{
	for(int i=1;i<=n;i++)
		tep[col][i]=tep[col][i]^1;
	cnt++;
}
void transfer_row(int rowa,int rowb)//tep
{
	for(int i=1;i<=m;i++)
	{
		tep[i][rowa]+=tep[i][rowb];
		tep[i][rowb]=tep[i][rowa]-tep[i][rowb];
		tep[i][rowa]-=tep[i][rowb];
	}
	if(rowa!=rowb)
		cnt++;
} 
bool same_row(int rowa,int rowb)//tep && des
{
	for(int i=1;i<=m;i++)
		if(tep[i][rowa]!=des[i][rowb])
			return false;
	return true;
}
void copymatrix()//give inti to tep
{
	for(int i=1;i<=m;i++)
		for(int j=1;j<=n;j++)
			tep[i][j]=inti[i][j];
}
void setup()
{
	scanf("%d%d",&m,&n);
	for(int i=1;i<=m;i++)
		for(int j=1;j<=n;j++)
			scanf("%d",&inti[i][j]);
	for(int i=1;i<=m;i++)
		for(int j=1;j<=n;j++)
			scanf("%d",&des[i][j]);
}
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	scanf("%d",&terms);
	int res=m+n+1;
	while(terms--)
	{
		setup();
		for(int i=1;i<=n;i++)
		{
			cnt=0;
			res=n+m+1;
			int flag=0;
			copymatrix();
			if(i-1)
				transfer_row(1,i);
			for(int j=1;j<=m;j++)
				if(tep[j][1]!=des[j][1])
					transfer_column(j);
			for(int j=1;j<=n;j++)//注意 
			{
			 	flag=0;
				for(int k=j;k<=n;k++)//這裡是從j開始而不是j+1,是為了比較這一列和目標矩陣中是否相同 
					if(same_row(j,k)) {transfer_row(j,k),flag=1;break;}
				if(!flag) break;
			}
			if(flag && cnt<n+m+1)
				res=res>cnt?cnt:res;
		}
		printf("%d\n",res==m+n+1?-1:res);
	}
	return 0;
}