• AtCoder Regular Contest 095E


    $n leq 12,m leq 12$,$n$行$m$列小写字母,现可做无数次操作:交换两行;交换两列。问是否有可能把他变成中心对称的。

    没有去想分组枚举的复杂度QAQ

    行和列的操作顺序是随意的。假如说在一种最优方案中,操作是行行行……行列列列……列行行行……行列列列……列,那您把后面那堆行和前面那堆列换一下准保没问题:在一行的字母是不会变成不在同一行的,在一列的字母是不会变成不在同一列的,所以我瞄准最优方案,先把行放好位置,然后调列,一定能调好。可能比较抽象,反正他是对的,具体证明出门见其他大牛博客。

    还有一件事,我枚举一种分配方案不需要枚举$n!$,很多都是重复的。怎么讲?先考虑这:我一种行的放法确定好,然后调列看合不合法。要合法的话,每个列都要和另一个列配上,这里的配上,就是一个取反后和另一个相等。如果m是奇数,那得有个列来当回文串放中间。这样子,假设现在放行的方案是$(p_1,p_2,...,p_n)$,如果咱变成$(p_2,p_1,...,p_n,p_{n-1})$,那跟前面那种是完全一样的(如果前面那种配上了这种也配上,如果前面那种配不上这种也配不上)。因此我只要把行打包成$n/2$对,然后每一对对称放,复杂度就会变成:多少啊,我每次从剩下的人里选一个考虑他配谁,选谁无所谓因为谁都要配,然后枚举他配谁,这样复杂度就是$(n-1)*(n-3)*...=(n-1)!!$。最大到10000多一点。行列都这么枚举加剪枝可以过。

    诶您啥啊列何必像行那样枚举,每次枚举配上就配上了不改了,因此列枚举复杂度可以变成$n*m*m$,排序+二分可以变成$n*m*log_2m$。

     1 //#include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 //#include<time.h>
     5 //#include<complex>
     6 //#include<set>
     7 #include<algorithm>
     8 //#include<math.h>
     9 #include<stdlib.h>
    10 using namespace std;
    11 
    12 int n,m;
    13 char mp[15][15];
    14 
    15 int a[15]; bool vis[15],ans=0;
    16 
    17 bool v2[15];
    18 bool check()
    19 {
    20 //    for (int i=1;i<=n;i++) cout<<a[i]<<' ';cout<<endl;
    21     memset(v2,0,sizeof(v2));
    22     bool used=(m&1);
    23     for (int i=1;i<=m;i++) if (!v2[i])
    24     {
    25         bool flag=0;
    26         for (int j=i+1;j<=m;j++) if (!v2[j])
    27         {
    28             bool ff=1;
    29             for (int k=1;k<=n;k++) if (mp[a[k]][i]!=mp[a[n-k+1]][j]) ff=0;
    30             if (ff) {v2[j]=1; flag=1; break;}
    31         }
    32         if (!flag)
    33         {
    34             if (used)
    35             {
    36                 bool ff=1;
    37                 for (int k=1;k<=n;k++) if (mp[a[k]][i]!=mp[a[n-k+1]][i]) ff=0;
    38                 if (ff) used=0;
    39                 else return 0;
    40             }
    41             else return 0;
    42         }
    43     }
    44     return 1;
    45 }
    46             
    47 void dfs(int cur,int pos,bool odd)
    48 {
    49     if (ans) return;
    50     if (cur>n) {ans=check(); return;}
    51     if (vis[cur]) {dfs(cur+1,pos,odd); return;}
    52     
    53     for (int i=cur+1;i<=n;i++) if (!vis[i])
    54     {
    55         a[pos]=i; a[n-pos+1]=cur;
    56         vis[i]=1;
    57         dfs(cur+1,pos+1,odd);
    58         vis[i]=0;
    59     }
    60     if (odd)
    61     {
    62         a[(n>>1)+1]=cur;
    63         dfs(cur+1,pos,0);
    64     }
    65 }
    66 
    67 int main()
    68 {
    69     scanf("%d%d",&n,&m);
    70     for (int i=1;i<=n;i++) scanf("%s",mp[i]+1);
    71     dfs(1,1,n&1);
    72     puts(ans?"YES":"NO");
    73     return 0;
    74 }
    View Code
  • 相关阅读:
    前端开发规范:命名规范、html 规范、css 规范、js 规范
    node.js入门系列(一)--Node.js简介
    浅析前端开发中的 MVC/MVP/MVVM 模式
    网页加载进度条的实现
    JavaScript深入系列(一)--原型和原型链详解
    vue2.0项目实战(5)vuex快速入门
    既然写CSS很容易,那为什么大家还是把CSS写的那么烂呢?
    vue2.0项目实战(4)生命周期和钩子函数详解
    vue2.0项目实战(3)使用axios发送请求
    【css技能提升】完美的 Sticky Footer 布局
  • 原文地址:https://www.cnblogs.com/Blue233333/p/8849571.html
Copyright © 2020-2023  润新知