11H:迷宫入口

总时间限制: 
1000ms
 
内存限制: 
10000kB
描述

爱好探险的你,找到一座装满了宝藏的迷宫的入口,你看到入口的大门上有一个边长为s的正方形的大锁,旁边散落着n块正方形的金属小片,你意识到锁的钥匙,即是用这n小块,拼成大门上的正方形,你想知道是否能拼成这把钥匙打开迷宫的大门。

输入
输入包含多组数组,第一行是一个整数t(1 <= t <= 10),表示有t组数据。接下里每组数组包含整数s,即锁的边长,整数n(1 <= n <= 16),即金属小片的数量,接下来n个整数,分别是各个小片的边长ci(1 <= ci <= 10)。
输出
每组数据输出一行,输出“YES"或者"NO",表示是否可以打开大门。
样例输入
2
4 8 1 1 1 1 1 3 1 1
5 6 3 3 2 1 1 1
样例输出
YES
NO
 1 #include <iostream>
 2 #include <vector>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 int a[15];
 7 int c[17];
 8 int m[45][45];
 9 int s, n;
10 void fill(int x, int y, int L, int vol){ //以(x,y)左上角填充内容为vol的正方形 
11     for(int i = x; i < x+L; i++)
12         for(int j = y; j < y+L; j++) 
13             m[i][j] = vol;
14 }
15 bool dfs(int x,int y){
16     if(m[x][y]){
17         if(y==s-1){
18             if(x==s-1)return 1;
19             else return dfs(x+1,0);    
20         }
21         else return dfs(x,y+1);
22     }
23     bool ret=0;
24     for(int L=1;!ret;L++){
25         if(y+L>s||x+L>s||m[x][y+L-1])break;
26         if(!c[L])continue;
27         c[L]--;
28         fill(x,y,L,1);
29         ret|=dfs(x,y);
30         fill(x,y,L,0);
31         c[L]++;
32     }
33     return ret;
34 }
35 
36 int main(){
37     int t;
38     cin>>t;
39     while(t--){
40         cin>>s>>n;
41         memset(a, 0, sizeof(a));
42         memset(c, 0, sizeof(c));
43         memset(m, 0, sizeof(m));
44         for(int i = 1; i <= n; i++){
45             cin>>a[i];
46             c[a[i]] ++;
47         }
48         if(s>40){
49             cout<<"NO"<<endl;
50             return 0;
51         }
52         bool ans = dfs(0, 0);
53         if(ans) cout<<"YES"<<endl;
54         else cout<<"NO"<<endl;
55     }
56     return 0;
57 }

备注:这差不多也就是标答,这么暴力居然也可以过orz 另外我怀疑这道题数据范围写错了,因为l和c数组开到40+才能过。开小了就是WA问了助教,等待答复中。。

大于四十不可能是因为,16个片,每个边长最大为10(这么看数据范围又没错?),加在一起面积也就是1600,也就是说锁的边长大于40就不可能凑出来了。当然不写这个也可以AC。

然后这个dfs的写法就 非常神奇……从左上角开始填充,先填充到最右,然后换行。标黄的地方我还是没太明白,wsl。我明白了。

因为前面说的填充顺序的问题,这时候填完了第一行的正方形,该填第二行了。这时候边长是从小往大尝试的,这很重要,所以x+L或者y+L出界了肯定不行要break这是显然的;还有就是如果像途中虚线正方形一样,如果它右上角已经开始侵占了第一行的正方形的位置(之前填充过)也说明边长太大了,要break。

为啥这么难。


这个dfs也可以不这么写,用更常规的方法是这样的:

 1 #include <iostream>
 2 #include <vector>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 int a[45];
 7 int c[45];
 8 int m[45][45];
 9 int s, n;
10 void fill(int x, int y, int L, int vol){ //以(x,y)左上角填充内容为vol的正方形
11     for(int i = x; i < x+L; i++)
12         for(int j = y; j < y+L; j++)
13             m[i][j] = vol;
14 }
15 bool dfs(int x,int y){
16     if(m[x][y]){
17         if(y==s-1){
18             if(x==s-1)return 1;
19             else return dfs(x+1,0);
20         }
21         else return dfs(x,y+1);
22     }
23     //bool ret=0;
24     for(int L=1;;L++){
25         if(y+L>s||x+L>s||m[x][y+L-1])break;
26         if(!c[L])continue;
27         c[L]--;
28         fill(x,y,L,1);
29         if(dfs(x,y)) return true;
30    //     ret|=dfs(x,y);
31         fill(x,y,L,0);
32         c[L]++;
33     }
34     return false;
35 }
36 
37 int main(){
38     int t;
39     cin>>t;
40     while(t--){
41         cin>>s>>n;
42         memset(a, 0, sizeof(a));
43         memset(c, 0, sizeof(c));
44         memset(m, 0, sizeof(m));
45         for(int i = 1; i <= n; i++){
46             cin>>a[i];
47             c[a[i]] ++;
48         }
49         if(s>40){
50             cout<<"NO"<<endl;
51             return 0;
52         }
53         bool ans = dfs(0, 0);
54         if(ans) cout<<"YES"<<endl;
55         else cout<<"NO"<<endl;
56     }
57     return 0;
58 }
原文地址:https://www.cnblogs.com/fangziyuan/p/13150020.html