AtCoder Regular Contest 095E - Symmetric Grid

$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

 

posted @ 2018-04-15 21:26  Blue233333  阅读(307)  评论(0编辑  收藏  举报