P16621 [GKS 2017 #C] X Squared 题解
题意分析
给你一个由字符 X 和 . 组成的 \(N\times N\) 棋盘,其中有 \(2\times N-1\) 个 X,\(N\) 为奇数,问是否能通过若干步使所有的 X 都在棋盘的对角线上,每一步可使任意两行或两列交换。
算法分析
首先我们观察这个图,可以发现除了中间那一行,其余行是两两相等的,列同理。
然后我们还能发现,行和行无论怎么交换,只要原图是合法的,就会发生只有一行与其他行都不一样,然后其余行两两相等,列也同理。
此外,还能发现那个单独的一行绝对只有一个 X,列同理。
所以,只要枚举每一 X 数量不为 \(1\) 的行,看是否只有一个除自己外与自己 X 分布相同的行,如果不是有且仅有一个,就不能达到目标,列同理。
时间复杂度:\(O(n^3)\)。
#include<bits/stdc++.h>
using namespace std;//参考代码
const int N=65;
bool a[N][N];//将字符转换成bool类型
int h[N],l[N];//每一行每一列有几个 X
int main(){
std::ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int T,id=0;
cin>>T;
while(T--){
int n;
cin>>n;
for(int i=1;i<=n;i++)h[i]=l[i]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
char x;
cin>>x;
if(x=='X')a[i][j]=1;
else a[i][j]=0;
if(a[i][j]){//如果这个点是 X
h[i]++,l[j]++;
}
}
}
bool ans=0;//计算答案
for(int i=1;i<=n;i++){//枚举每一行
if(h[i]==1)continue;//如果是单独行
int num1=0;
for(int j=1;j<=n;j++){
if(i==j)continue;
bool f=0;
for(int k=1;k<=n;k++){//判断两行是否相等
if(a[i][k]!=a[j][k])f=1;
}
if(!f)num1++;
}
if(num1!=1){//如果不是有且仅有一个
ans=1;
}
}
for(int i=1;i<=n;i++){//枚举每一列
if(l[i]==1)continue;//同上
int num1=0;
for(int j=1;j<=n;j++){
if(i==j)continue;
bool f=0;
for(int k=1;k<=n;k++){
if(a[k][i]!=a[k][j])f=1;
}
if(!f)num1++;
}
if(num1!=1){
ans=1;
}
}
cout<<"Case #"<<++id<<": ";
if(!ans)cout<<"POSSIBLE\n";
else cout<<"IMPOSSIBLE\n";
}
return 0;
}
由于 \(N≤55\),所以我们可以用二进制来存储每一行的 X 分布的状态,从而优化判断两行是否相等的过程。
时间复杂度:\(O(n^2)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=65;
long long hs[N],ls[N],h[N],l[N],a[N][N];//注意开long long
int main(){
std::ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int T,id=0;
cin>>T;
while(T--){
int n;
cin>>n;
for(int i=1;i<=n;i++)h[i]=l[i]=hs[i]=ls[i]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
char x;
cin>>x;
if(x=='X')a[i][j]=1;
else a[i][j]=0;
if(a[i][j]){
h[i]++,l[j]++;
}
}
}
for(int i=1;i<=n;i++){
for(long long j=1;j<=n;j++){//枚举第i行的每一列
if(a[i][j])hs[i]+=(1ll<<(j-1ll));
}
for(long long j=1;j<=n;j++){//枚举第i列的每一行
if(a[j][i])ls[i]+=(1ll<<(j-1ll));
}
}
bool ans=0;
for(int i=1;i<=n;i++){
if(h[i]==1)continue;//跟上份代码一样
int num1=0;
for(int j=1;j<=n;j++){
if(i==j)continue;
if(hs[i]==hs[j])num1++;//如果两行状态相同
}
if(num1!=1){
ans=1;
}
}
for(int i=1;i<=n;i++){
if(l[i]==1)continue;
int num1=0;
for(int j=1;j<=n;j++){
if(i==j)continue;
if(ls[i]==ls[j])num1++;//同上
}
if(num1!=1){
ans=1;
}
}
cout<<"Case #"<<++id<<": ";
if(!ans)cout<<"POSSIBLE\n";
else cout<<"IMPOSSIBLE\n";
}
return 0;
}

浙公网安备 33010602011771号