HDU 3121 FreeOpen
被这题整整搞了两天啊,先是官方题解:
Freeopen
标程做法是IDA* H函数为三次二分图匹配. 搜索顺序按度来优化.
这个题生成数据的时候光在关注怎么卡掉一般的搜索了.写了六七个搜索,分别用十个数据让这几个搜索超时.
但是数据量太少了,导致有一些莫名其妙的网络流AC了.
此题是npc问题,可以划归到3-sat.所以应该不存在多项式算法.
话说这题数据确实太烂了,这种题目单靠手造数据很难卡掉所有非标程做法的,不但没卡掉别的方法,按标程方法不加强减枝也很难过掉了
话说过来用这题来练习IDA*和搜索减枝确实不错,一般的IDA*是从少层向多层去搜,这题求最大值,就要IDA*从多向少去搜了,构造h()函数要满足大于等于实际值,从多到少的IDA*并不是一个很实用的方法,层数越多搜到的状态数越多,就像IDA*用二分更慢一样,不过这也是一种IDA*新的使用方法,偶尔会派上用场吧。
因为这题看起来就和二分匹配很有关系(我可以保证如果比赛时遇到这题我绝对不会向搜索方向去想,或许是二分匹配变形或是网络流什么的),如果想到A*搜索的话用二分图匹配也正常,三次二分图匹配作为A*函数是很大胆的一种做法,这么高复杂度的算法用在频繁调用的h()中就必须要保证他的减枝的有效性。三次二分匹配分别做女生和男生,女生和宠物,男生和宠物,取最小值(多次数据测试证明,第一次二分匹配会使速度变慢),如果这个值加上步数小于上限则跳出。
至于按度优化没什么可说的,只要不按正序基本都能过了,按度反序都能过的 = =||,我更不会告诉你,按照输入顺序反序15ms哦~~
然后就是各种减枝了...
写了好久的代码,但由于能力有限,效率很低,500+ms,由于数据问题,可能改一个很小的,甚至无关的地方都可能使时间突变...
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 using namespace std;
5 #define MIN(x,y) (x<y?x:y)
6
7 int g,b,p;
8 int G,B,P;
9 int gdeg[22];
10 int grank[22];
11 int gb[22],gp[22],bp[22];
12 bool in(){
13 char c=getchar();
14 while(c<=32) c=getchar();
15 return (c=='1');
16 }
17
18 int vis;
19 int mat[22];
20 inline bool find0(int u){
21 for(int i=0;i<b;i++){
22 if(B&(1<<i)) continue;
23 if(!(gb[u]&(1<<i))) continue;
24 if(vis&(1<<i)) continue;
25 vis|=(1<<i);
26 if(mat[i]==-1||find0(mat[i])){
27 mat[i]=u;
28 return true;
29 }
30 }
31 return false;
32 }
33
34 inline bool find1(int u){
35 for(int i=0;i<p;i++){
36 if(P&(1<<i)) continue;
37 if(!(gp[u]&(1<<i))) continue;
38 if(vis&(1<<i)) continue;
39 vis|=(1<<i);
40 if(mat[i]==-1||find1(mat[i])){
41 mat[i]=u;
42 return true;
43 }
44 }
45 return false;
46 }
47
48 inline bool find2(int u){
49 for(int i=0;i<p;i++){
50 if(P&(1<<i)) continue;
51 if(!(bp[u]&(1<<i))) continue;
52 if(vis&(1<<i)) continue;
53 vis|=(1<<i);
54 if(mat[i]==-1||find2(mat[i])){
55 mat[i]=u;
56 return true;
57 }
58 }
59 return false;
60 }
61
62 int dep;
63 inline bool h(int d,int tg){
64 int cnt=0;
65 memset(mat,-1,sizeof(mat));
66 for(int i=tg;i<g;i++){
67 vis=0;
68 if(find0(grank[i])) cnt++;
69 if(cnt>dep-d) break;
70 }
71 if(cnt+d<dep) return false;
72
73 cnt=0;
74 memset(mat,-1,sizeof(mat));
75 for(int i=tg;i<g;i++){
76 vis=0;
77 if(find1(grank[i])) cnt++;
78 if(cnt>dep-d) break;
79 }
80 if(cnt+d<dep) return false;
81
82 cnt=0;
83 memset(mat,-1,sizeof(mat));
84 for(int i=0;i<b;i++){
85 if(B&(1<<i)) continue;
86 vis=0;
87 if(find2(i)) cnt++;
88 if(cnt>dep-d) break;
89 }
90 if(cnt+d<dep) return false;
91
92 return true;
93 }
94
95 bool dfs(int d,int tg){
96 if(d==dep) return true;
97 if(!h(d,tg)) return false;
98
99 for(int ii=tg;ii<g;ii++){
100 int i=grank[ii];
101 G|=(1<<i);
102 for(int j=0;j<b;j++){
103 if(B&(1<<j)) continue;
104 if(!(gb[i]&(1<<j))) continue;
105 B|=(1<<j);
106 for(int k=0;k<p;k++){
107 if(P&(1<<k)) continue;
108 if(gp[i]&bp[j]&(1<<k)){
109 P|=(1<<k);
110 if(dfs(d+1,ii+1)) return true;
111 P^=(1<<k);
112 }
113 }
114 B^=(1<<j);
115 }
116 G^=(1<<i);
117 }
118 return false;
119 }
120
121 bool gcmp(const int& a,const int& b){
122 return gdeg[a]<gdeg[b];
123 }
124
125 int main(){
126 int t;
127 scanf("%d",&t);
128 while(t--){
129 scanf("%d%d%d",&g,&b,&p);
130 memset(gb,0,sizeof(gb));
131 memset(gp,0,sizeof(gp));
132 memset(bp,0,sizeof(bp));
133 memset(gdeg,0,sizeof(gdeg));
134 for(int i=0;i<g;i++){
135 for(int j=0;j<b;j++){
136 gb[i]|=(in()<<j);
137 }
138 }
139 for(int i=0;i<g;i++){
140 for(int j=0;j<p;j++){
141 gp[i]|=(in()<<j);
142 }
143 }
144 for(int i=0;i<b;i++){
145 for(int j=0;j<p;j++){
146 bp[i]|=(in()<<j);
147 }
148 }
149
150 for(int i=0;i<g;i++){
151 grank[i]=i;
152 for(int j=0;j<b;j++){
153 if(!(gp[i]&bp[j])) gb[i]&=~(1<<j);
154 if(gb[i]&(1<<j)) gdeg[i]++;
155 }
156 }
157 sort(grank,grank+g,gcmp);
158 //reverse(grank,grank+g);
159
160 G=B=P=0;
161 dep=MIN(MIN(g,b),p);
162 while(true){
163 if(dfs(0,0)) break;
164 dep--;
165 }
166
167 printf("%d\n",dep);
168 }
169 }