10.04 T2 搜索+乱搞剪枝

Description

Input

Output

输出T行,每一行一个YES或NO,表示是否能够使图形中心对称。

Sample Input

6 1 2 3 ABC BAC

Sample Output

YES

Hint

【数据范围及约定】
 
 
 
 
法1:我的迭代加深,实测70分
code:
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<string>
 4 using namespace std;
 5 int m,n;
 6 char g[20][20];
 7 void hang(int a,int b){
 8     for(int i=1;i<=m;i++)swap(g[a][i],g[b][i]);    
 9 }
10 void lie(int a,int b){
11     for(int i=1;i<=n;i++)swap(g[i][a],g[i][b]);
12 }
13 void prt(){
14     for(int i=1;i<=n;i++){
15         for(int j=1;j<=m;j++){
16             cout<<g[i][j]<<" ";
17         }
18         cout<<'\n';
19     }
20 }
21 bool dfs(int dep,int limit){
22 //    cout<<"dep->"<<dep<<" limit->"<<limit<<'\n'<<endl;
23     if(dep==limit){
24         for(int i=1;i<=n;i++){
25             for(int j=1;j<=m;j++){
26                 if(g[i][j]!=g[n-i+1][m-j+1])return false;
27             }
28         }
29     //    prt();
30         return true;
31     }
32     for(int i=1;i<=n;i++){
33         for(int j=i+1;j<=n;j++){
34             hang(i,j);
35             if(dfs(dep+1,limit))return true;
36             hang(i,j);
37         }
38     }
39     for(int i=1;i<=m;i++){
40         for(int j=i+1;j<=m;j++){
41             lie(i,j);
42             if(dfs(dep+1,limit))return true;
43             lie(i,j);
44         }
45     }
46     return false;
47 }
48 int main(){
49     freopen("fragment.in","r",stdin);
50     freopen("fragment.out","w",stdout);
51     int num,T;
52     cin>>num>>T;
53     while(T--){
54         cin>>n>>m;
55         string temp;
56         for(int i=1;i<=n;i++){
57             cin>>temp;
58             for(int j=1;j<=m;j++){
59                 g[i][j]=temp[j-1];
60             }
61         }
62         int d=0;
63         while(1){
64             if(d>=5){
65                 cout<<"NO"<<endl;
66                 break;
67             }
68             if(dfs(0,d)==true){
69                 cout<<"YES"<<'\n';
70                 break;
71             }
72             d++;
73         //    cout<<d<<'\n';
74         }
75     }
76     return 0;
77 }

有一个很显然的性质,每一行每一列的元素组成都是不变的

所以我们先暴力每一行的情况,利用这一性质剪枝,也就是每一行与其相对的行的元素组成

是相同的,然后判断列是否满足。

有如下观察:

1、同一行/列的元素(可重)集合不变。

2、允许任意交换意味着我们可以随意排列行、列的顺序。

3、对称的两行/列元素(可重)集合相同。

4、对于已有的一个可行解,若其两组对称的行/列为 A和 A′、B 和 B′那么我们交换

A 和 B,A′和 B′后,它依然是一组合法解。

考虑枚举。若 N 为奇数,那么我们就先枚举中间的一行/列对应原矩阵的哪一行/列,这样,

剩余的行/列数变成了偶数。

考虑一次枚举对称的两行/列分别对应原矩阵的哪一行/列,由观察 4,我们不妨任取一个没

有被选取过的行/列,再枚举一个与之对称的行/列。

对于单次对行/列的排列的枚举,至多有 M=1!=11*9*7*5*3*1=10395 种可能的情况。

考虑预处理出每两行/列元素(可重)集合是否相同,并利用观察 3 进行剪枝。

直接枚举行和列的最终排列,并暴力判断方案的合法性,时间复杂度 O(H*W*M^2)。

但由于利用观察 3 进行的剪枝,无解的情况 M 都只会在在很小的范围内,而有解的情况在M 较大时往往情况简单(例如所有元素均相同),很容易就能够找到一组解,并结束搜索,因此,该算法的实际运行效率甚至比正解更好(最慢 2ms),以下代码实现了该算法。

正解的做法是先枚举行的最终排列,然后将列两两匹配,若能够剩余不足两列(一列或零列),

那么就找到了一组解。

时间复杂度 O(H*W^2*M)或 O(H*WLogW*M)。

code:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<string>
  4 #include<cstring>
  5 #define mem(i) memset(i,0,sizeof i)
  6 using namespace std;
  7 int n,m;
  8 int g[15][15],origin_hang[15],origin_lie[15],flag_of_solution,jihehang[15][27],jihelie[15][27];
  9 int used_lie[15],used_hang[15],equal_lie[15][15],equal_hang[15][15];
 10 void inital_array(){
 11     mem(origin_hang),mem(origin_lie),flag_of_solution=0,mem(jihehang),mem(jihelie),mem(used_lie);
 12     mem(used_hang),mem(equal_lie),mem(equal_hang);
 13 }
 14 bool check_suc(){
 15     for(int i=1;i<=n;i++){
 16         for(int j=1;j<=m;j++){
 17             if(g[origin_hang[i]][origin_lie[j]]!=g[origin_hang[n-i+1]][origin_lie[m-j+1]])return false;
 18         }
 19     }
 20     flag_of_solution=1;
 21     return true;
 22 }
 23 bool check_equal_hang(int a,int b){
 24     for(int i=1;i<=26;i++){
 25         if(jihehang[a][i]!=jihehang[b][i])return false;
 26     }
 27     return true;
 28 }
 29 bool check_equal_lie(int a,int b){
 30     for(int i=1;i<=26;i++){
 31         if(jihelie[a][i]!=jihelie[b][i])return false;
 32     }
 33     return true;
 34 }
 35 void dfsY(int now,int flag_odd){
 36     if(flag_odd&&now==m/2+1){
 37         for(int i=1;i<=m;i++){
 38             origin_lie[now]=i;
 39             used_lie[i]=1;
 40             dfsY(now-1,0);        
 41             used_lie[i]=0;
 42             origin_lie[now]=0;
 43             if(flag_of_solution)return;
 44         }
 45         return;
 46     }
 47     if(now==0){
 48         check_suc();
 49         return;
 50     }
 51     for(int i=1;i<=m;i++){
 52         if(used_lie[i])continue;
 53         origin_lie[now]=i;
 54         used_lie[i]=1;
 55         for(int j=1;j<=m;j++){
 56             if(i==j)continue;
 57             if(used_lie[j])continue;
 58             if(equal_lie[i][j]){
 59                 origin_lie[m+1-now]=j;
 60                 used_lie[j]=1;
 61                 dfsY(now-1,0);
 62                 used_lie[j]=0;
 63                 origin_lie[m+1-now]=0;
 64                 if(flag_of_solution)return;
 65             }
 66         }
 67         origin_lie[now]=0;
 68         used_lie[i]=0;
 69     }
 70 }
 71 void dfsX(int now,int flag_odd){
 72     if(flag_odd&&now==n/2+1){//如果行数是奇数,并且现在在讨论中间的情况 
 73         for(int i=1;i<=n;i++){
 74             origin_hang[now]=i; 
 75             used_hang[i]=1;
 76             dfsX(now-1,0);
 77             used_hang[i]=0;
 78             origin_hang[now]=0;
 79             if(flag_of_solution)return;
 80         }
 81         return;
 82     }
 83     if(now==0){
 84         dfsY((m+1)/2,m&1);
 85         return;
 86     }
 87     for(int i=1;i<=n;i++){//现在不是中间,我们要找一个还没有使用过的集合来填进去 
 88         if(used_hang[i])continue;//如果这个行已经使用过了,continue 
 89         origin_hang[now]=i;
 90         used_hang[i]=1;
 91         for(int j=1;j<=n;j++){
 92             if(i==j)continue;
 93             if(used_hang[j])continue;
 94             if(equal_hang[i][j]){
 95                 origin_hang[n+1-now]=j;
 96                 used_hang[j]=1;
 97                 dfsX(now-1,0);
 98                 used_hang[j]=0;
 99                 origin_hang[n+1-now]=j;
100                 if(flag_of_solution)return;
101             }
102         }
103         origin_hang[now]=0;
104         used_hang[i]=0;
105     }
106 }
107 int main(){
108     freopen("fragment.in","r",stdin);
109     freopen("fragment.out","w",stdout);
110     int num;cin>>num;
111     int T;
112     cin>>T;
113     while(T--){
114         inital_array();
115         cin>>n>>m;
116         string temp;
117         for(int i=1;i<=n;i++){//预处理行列里面每个字母出现的次数和集合 
118             cin>>temp;
119             for(int j=1;j<=m;j++){
120                 g[i][j]=temp[j-1];
121                 jihehang[i][g[i][j]-'A'+1]++;
122                 jihelie[j][g[i][j]-'A'+1]++;
123             }
124         }
125         for(int i=1;i<=n;i++){//计算行列之间是不是互相对应的 
126             for(int j=i+1;j<=n;j++){
127                 equal_hang[i][j]=equal_hang[j][i]=check_equal_hang(i,j);
128             }
129         }
130         for(int i=1;i<=m;i++){
131             for(int j=i+1;j<=m;j++){
132                 equal_lie[i][j]=equal_lie[j][i]=check_equal_lie(i,j);
133             }
134         }
135         dfsX((n+1)/2,n&1);
136         if(flag_of_solution)cout<<"YES"<<'\n';
137         else cout<<"NO"<<'\n';
138     }
139     return 0;
140 }

本地过了就是过了,超时说明OJ有问题

over

posted @ 2018-10-04 16:00  saionjisekai  阅读(41)  评论(0编辑  收藏  举报