洛古 P1312 Mayan游戏(dfs+剪枝)

题目链接

这道题和俄罗斯方块很像

很明显,我们可以看出这是一个dfs,但是,我们需要几条剪枝:

1.如果只剩下1个或2个同样颜色的方块,那么直接退出

2.相同的块不用交换

3.注意优先性,优先左边换右边

但是这题就这么样就完了吗

显然,并没有这么简单:剪枝清楚了,你确定就能写出来吗(这是我写过最长的dfs)

接下来讲一下我的程序架构:

init函数:输入,存储数据

fall函数:模拟方块下落

printans函数:输出答案

clear函数:清除棋盘

isempty函数:判断是否为空

judge函数:剪枝1

dfs函数:搜索

下面是代码,看不懂上面的话,代码里也有注释

 

 

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 
  4 const int N = 5;
  5 const int M = 7;
  6 
  7 int maxstep, chess[N][M];
  8 int cnt[11];
  9 int answer[5][3];
 10 
 11 void init()
 12 {
 13     cin>>maxstep;
 14     for(int i=0; i<N; i++)
 15     {
 16         int j=0,x;
 17         cin>>x;
 18         while(x!=0)
 19         {
 20             chess[i][j]=x;
 21             j++;
 22             cin>>x;
 23         }
 24     }
 25 }
 26 
 27 void fall()    //清除后方块下落
 28 {
 29     for(int i=0; i<N; i++)
 30         for(int j=0; j<M; j++)
 31         {
 32             if(chess[i][j]!=0)
 33                 continue;
 34             int k;
 35             for(k=j+1; k<M; k++)
 36                 if(chess[i][k]!=0)
 37                 {
 38                     swap(chess[i][j],chess[i][k]);
 39                     break;
 40                 }
 41             if(k==M)
 42                 break;
 43         }
 44 }
 45 
 46 void printans()     //输出
 47 {
 48     for(int i=0; i<maxstep; i++)
 49         printf("%d %d %d\n",answer[i][0],answer[i][1],answer[i][2]);
 50     return;
 51 }
 52 
 53 bool clear()        //清除棋盘
 54 {
 55     bool empty[N][M];
 56     memset(empty,false,sizeof(empty));
 57     for(int i=0; i<N-2; i++)
 58         for(int j=0; j<M; j++)
 59             if(chess[i][j]!=0 && chess[i][j]==chess[i+1][j] 
 60                 && chess[i+1][j]==chess[i+2][j]) {
 61                     empty[i][j]=empty[i+1][j]=empty[i+2][j]=true;
 62                 }
 63                 
 64 
 65     for(int i=0; i<N; i++)
 66         for(int j=0; j<M-2; j++)
 67             if(chess[i][j]!=0 && chess[i][j]==chess[i][j+1]
 68                 && chess[i][j+1]==chess[i][j+2]) {
 69                     empty[i][j]=empty[i][j+1]=empty[i][j+2]=true;
 70                 }
 71 
 72     bool res=false;
 73     for(int i=0; i<N; i++)
 74         for(int j=0; j<M; j++)
 75             if(empty[i][j])
 76             {
 77                 chess[i][j]=0;
 78                 res=true;
 79             }
 80     return res;
 81 }
 82 
 83 bool isempty()      // 判断棋盘是否为空
 84 {
 85     for(int i=0; i<N; i++)
 86         for(int j=0; j<M; j++)
 87             if(chess[i][j]!=0)
 88                 return false;
 89     return true;
 90 }
 91 
 92 bool judge()        //判断是否有一种颜色的块数量为1或2
 93 {
 94     memset(cnt,0,sizeof(cnt));
 95     for(int i=0; i<N; i++)
 96         for(int j=0; j<M; j++)
 97             cnt[chess[i][j]]++;
 98     for(int i=1; i<=10; i++)
 99         if(cnt[i]==1||cnt[i]==2)
100             return false;
101     return true;
102 }
103 
104 bool dfs(int step)      //搜索
105 {
106     if(isempty())       //发现一组解
107     {
108         printans();
109         return true;
110     }
111     if(step>=maxstep || !judge())     //剪枝与结束条件
112         return false; 
113 
114     int now[N][M];      // 记录当前状态,回溯时候用 
115     for(int i=0; i<N; i++)      //复制棋盘
116         for(int j=0; j<M; j++)
117             now[i][j]=chess[i][j];
118 
119     for(int i=0; i<N; i++)
120         for(int j=0; j<M; j++)      //枚举每一个方块
121         {
122             //右移
123             if(i!=N-1 && chess[i][j]!=0 && chess[i][j]!=chess[i+1][j])
124             {
125                 swap(chess[i][j], chess[i+1][j]);
126                 answer[step][0]=i;
127                 answer[step][1]=j;
128                 answer[step][2]=1;
129                 fall();
130                 while(clear())
131                     fall();
132                 if(dfs(step+1))
133                     return true;
134             }
135 
136             for(int k1=0; k1<N; k1++)
137                 for(int k2=0; k2<M; k2++)
138                     chess[k1][k2]=now[k1][k2];
139 
140             //左移
141             if(i!=0 && chess[i][j]!=0 && chess[i-1][j]==0)
142             {
143                 swap(chess[i][j], chess[i-1][j]);
144                 answer[step][0]=i;
145                 answer[step][1]=j;
146                 answer[step][2]=-1;
147                 fall();
148                 while(clear())
149                     fall();
150                 if(dfs(step+1))
151                     return true;
152             }
153 
154             for(int k1=0; k1<N; k1++)
155                 for(int k2=0; k2<M; k2++)
156                     chess[k1][k2]=now[k1][k2];
157         }
158     return false;
159 }
160 
161 /*
162 搜索+剪枝 
163 剪枝原则:
164 1,交换两个相同颜色的块没有意义。
165 2,如果一种颜色的块数量为1或2,则当前局面无解。
166 3,因为要求字典序最小的解,所以一个块仅当左边为空时尝试左移
167 (若左面有方块,那么在搜i-1列时将其右移,和在i列时左移是等效的,故可以剪枝) 
168 */ 
169 int main()
170 {
171     ios::sync_with_stdio(false);
172     cin.tie(0);
173     
174     init();
175     if(!dfs(0))
176         cout<<-1<<endl;
177     return 0;
178 }

 

补充:说到俄罗斯方块,我又想起来USACO的这道题,有兴趣的同学可以去做做

 

posted @ 2020-03-22 20:46  橙剑oo  阅读(176)  评论(0编辑  收藏  举报