VEXOBEN
Vexoben
Apr 15, 2018
It takes 4 minutes to read this article.

题目链接:https://arc095.contest.atcoder.jp/tasks/arc095_c

不是很难的一道搜索题,感觉搜索不怎么会……

题意

给你一个n*m的矩阵,每个格点中有一个小写字母。每次可以交换两行或两列,问是否可以使这个矩阵中心对称。

n,m<=12

题解

思路是暴枚行的排布方式,然后判列是否能合法交换。

对于已经确定好的行排布,如果两列正好相反(s[i][x]==s[n-i+1][y]),那么这两列可以匹配,我们直接匹配他们。

如果有一列不能和其他列匹配,如果列数是奇数,我们看它能否和自己匹配(就是放在中间的那一列)。

注意两个细节:

1、暴枚行的排布时,我们枚举它和哪一行匹配。这样我们就只需要枚举119753*1次。

2、单独的列要求能够和自己匹配(事实上不判这个也能过)。但是暴枚行排布时单独的行不需要能和自己匹配。

注释中的数据是atc上AC了之后把自己叉掉的数据……

#include<bits/stdc++.h>
using namespace std;
const int N=15;

char a[N][N],b[N][N];
int n,m,match[N],use[N];

inline int Equal(int x,int y) {
	for (int i=1;i<=n;i++) {
		if (b[i][x]!=b[n-i+1][y]) return 0;
	}
	return 1;
}

inline void check() {
	int res=m&1;
	memset(use,0,sizeof(use));
	int now=1;
	for (int i=1;i<=n;i++) {
		if (i==match[i]) {
			for (int j=1;j<=m;j++)
				b[(n+1)>>1][j]=a[i][j];
		}
		if (i<match[i]) {
			for (int j=1;j<=m;j++) {
				b[now][j]=a[i][j];
				b[n-now+1][j]=a[match[i]][j];
			}
			++now;		
		}
	}		
	for (int i=1;i<=m;i++) {
		if (use[i]) continue;
		for (int j=i+1;j<=m;j++)
			if (!use[j]&&Equal(i,j)) {
				use[i]=j; use[j]=i;
				break;
			}
		if (!use[i]) {
			int flag=1;
			for (int j=1;j<=n;j++) 
				if (b[j][i]!=b[n-j+1][i]) flag=0;
			if (!flag) return;
			if (--res<0) return;
		}
	}
	puts("YES"); exit(0);
}

void dfs(int x,int res) {
	if (x==n+1) return (void) (check());
	if (match[x]) return (void) (dfs(x+1,res));
	if (res) {
		match[x]=x;
		dfs(x+1,0);
		match[x]=0;
	}
	for (int i=x+1;i<=n;i++) {
		if (!match[i]) {
			match[i]=x; match[x]=i;
			dfs(x+1,res);
			match[i]=0; match[x]=0;
		}
	}
}

int main() {
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%s",a[i]+1);
	dfs(1,n&1);
	puts("NO");
	return 0;
}
/*
3 3
aac
bab
cba
*/