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;
}
posted @ 2026-10-17 19:07  SCX2014  阅读(2)  评论(0)    收藏  举报