【分析】
建议先手动模拟着玩玩看,哈~
结果发现这挺像跳竹竿,相对于每一列,我们交换任意两列,不会改变任何一列的数列排布,交换任意两行,只会改变每两列中两个元素的位置。类似的,我们也可以得到关于行的结论。综上,任意的操作不会改变任何一行一列的数的种类和数目。
简直是句废话这事一个很有用的约束条件。将他推广,我们可以简单判定,每个矩阵只能有一行和一列是无法配对的,其余行列必须有和它完全相同的列或行进行两两匹配。
如果就这样交上去,你可以获得90分的好成绩。分析原因,是我们把其他一些可能不行的情况算成可以了。于是乎,我们把原来的判定改写为初始化,再通过暴力dfs重构可配对的行和列,判断是否可行。
骗分90的神奇代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
char g[20][20];
int cnt[50][20],ok;
bool used[20];
int Num,T,n,m;
bool check1(int x,int y){
for(int i=0;i<26;i++)
if(cnt[i][x]!=cnt[i][y]) return 0;
return 1;
}
bool check(){
memset(cnt,0,sizeof(cnt));
memset(used,0,sizeof(used));
for(int i=0;i<n;i++){
for(int j=0;j<m;j++)
cnt[g[i][j]-'A'][i]++;
}
int ret=0;
for(int i=0;i<n;i++){
if(used[i]) continue;
bool flag=0;
for(int j=i+1;j<n;j++){
if(used[j]) continue;
if(check1(i,j)) used[i]=1,used[j]=1,flag=1;
}
if(flag==0)ret++;
}
if(ret>1) return 0;
memset(cnt,0,sizeof(cnt));
memset(used,0,sizeof(used));
for(int j=0;j<m;j++){
for(int i=0;i<n;i++)
cnt[g[i][j]-'A'][j]++;
}
ret=0;
for(int i=0;i<m;i++){
if(used[i]) continue;
bool flag=0;
for(int j=i+1;j<m;j++){
if(used[j]) continue;
if(check1(i,j)) used[i]=1,used[j]=1,flag=1;
}
if(flag==0)ret++;
}
if(ret>1) return 0;
return 1;
}
int main(){
freopen("fragment.in","r",stdin);
freopen("fragment.out","w",stdout);
cin>>Num>>T;
while(T--)
{
scanf("%d %d",&n,&m);
for(int i=0;i<n;i++)
scanf("%s",g[i]);
if(!check()){
printf("NO\n");
continue;
}
else printf("YES\n");
}
return 0;
}
看过标程洗心革面后的重构代码,(注释详细)
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 15;
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
int n, m;
char mp[MAXN][MAXN];
int rnum[MAXN], cnum[MAXN];
bool visrow[MAXN], viscol[MAXN], solved;
bool row[MAXN][MAXN], col[MAXN][MAXN];
void check() {
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (mp[rnum[i]][cnum[j]] != mp[rnum[n - i + 1]][cnum[m - j + 1]]) return;
printf("YES\n");
solved = true;
}
void workc(int pos, int type) {//同上
if (solved) return;//存在性剪枝
if (pos == 0) {
check();//矩阵完成后,判断是否可行
return;
}
if (type) {
for (int i = 1; i <= m; i++) {
viscol[i] = true;
cnum[pos] = i;
workc(pos - 1, 0);
viscol[i] = false;
}
} else {
for (int i = 1; i <= m; i++) {
if (viscol[i]) continue;
for (int j = i + 1; j <= m; j++)
if (!viscol[j] && col[i][j]) {
viscol[i] = true;
viscol[j] = true;
cnum[pos] = i;
cnum[m + 1 - pos] = j;
workc(pos - 1, 0);
viscol[i] = false;
viscol[j] = false;
}
return;
}
}
}
void workr(int pos, int type) {
if (solved) return;//存在性剪枝
if (pos == 0) {//转向判断列
workc((m + 1) / 2, m & 1);
return;
}
if (type) {//枚举奇数n中的无法配对行
for (int i = 1; i <= n; i++) {
visrow[i] = true;
rnum[pos] = i;
workr(pos - 1, 0);
visrow[i] = false;
}
} else {
for (int i = 1; i <= n; i++) {
if (visrow[i]) continue;
for (int j = i + 1; j <= n; j++)
if (!visrow[j] && row[i][j]) {//先将i行和j行配对
visrow[i] = true;
visrow[j] = true;
rnum[pos] = i;//放在矩阵最内行
rnum[n + 1 - pos] = j;
workr(pos - 1, 0);
visrow[i] = false;
visrow[j] = false;
}
return;
}
}
}
int main() {
int T, num; read(num), read(T);
while (T--) {
read(n), read(m);
for (int i = 1; i <= n; i++)
scanf("%s", mp[i] + 1);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
static char x[MAXN], y[MAXN];
for (int k = 1; k <= m; k++) {
x[k] = mp[i][k];
y[k] = mp[j][k];
}
sort(x + 1, x + m + 1);
sort(y + 1, y + m + 1);
row[i][j] = true;
for (int k = 1; k <= m; k++)
if (x[k] != y[k]) row[i][j] = false;
}//判断row[i]和row[j]是否匹配
for (int i = 1; i <= m; i++)
for (int j = 1; j <= m; j++) {
static char x[MAXN], y[MAXN];
for (int k = 1; k <= n; k++) {
x[k] = mp[k][i];
y[k] = mp[k][j];
}
sort(x + 1, x + n + 1);
sort(y + 1, y + n + 1);
col[i][j] = true;
for (int k = 1; k <= n; k++)
if (x[k] != y[k]) col[i][j] = false;
}//判断c[i]和c[j]是否匹配
solved = false;//不匹配
workr((n + 1) / 2, n & 1);//配对r数和n奇偶性
if (!solved) printf("NO\n");//如果没有方案
}
return 0;
}