P1312 [NOIP2011 提高组] Mayan 游戏

原题链接

考察:dfs + 剪枝

思路:

        搜索顺序是枚举每一个有颜色的格子左右移动的操作,时间复杂度是O(352*5).需要剪枝:

  1. 搜索顺序剪枝,这个应该没有
  2. 最优性剪枝.因为我们需要的是字典序最小的序列,也就是列坐标越小越好.可以先枚举列再枚举行,这样如果返回1就一定是最小的.
  3. 还有一个最优性剪枝:因为操作要列坐标尽可能小,所以某物体左移尽量换成该物体右移.
  4. 可行性剪枝:如果当前有颜色只剩下1 或 2个,立即return

        还有一个十分恶心的操作就是移动更新地图,本蒟蒻想半天不知道怎么回溯,结果是memcpy....

         更新地图基本照抄Y总代码,有个很好的优化是直接统计要消除的格子数,这样就不用遍历一遍.

         坑爹的是行列公用,所以不要一找到就消除.

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 using namespace std;
  5 const int N = 6,M = 8,K = 10;
  6 int m,mp[M][N],backup[N][M][N];
  7 int cnt[K],bcnt[N][K];
  8 bool st[M][N];
  9 struct Path{
 10     int x,y,d;
 11 }path[N];
 12 bool check()
 13 {
 14     for(int i=1;i<=10;i++)
 15       if(cnt[i]>0&&cnt[i]<3) return 0;
 16     return 1;
 17 }
 18 void move(int x,int y,int ny)
 19 {
 20     swap(mp[x][y],mp[x][ny]);
 21     while(1)//迭代是因为存在消掉又让一部分被削掉 
 22     {//处理悬空方块 
 23         for(int j=1;j<=5;j++)
 24         {
 25             int k = 0;
 26             for(int i=1;i<=7;i++)
 27               if(mp[i][j])
 28               {
 29                    mp[++k][j] = mp[i][j];
 30               }
 31             for(int i=k+1;i<=7;i++) mp[i][j] = 0;
 32         }
 33         bool ok = 0;
 34         memset(st,0,sizeof st);
 35         for(int i=1;i<=7;i++)
 36           for(int j=1;j<=5;j++)
 37             if(mp[i][j])
 38             {
 39                 int k = i,r = i;
 40                 while(k+1<=7&&mp[k][j]==mp[k+1][j]) k++;
 41                 while(r-1>=1&&mp[r][j]==mp[r-1][j]) r--;
 42                 if(k-r+1>=3)
 43                 {
 44                     ok = 1;
 45                     st[i][j] = 1;//看此方隔要不要消掉 
 46                 }
 47                 else{
 48                     k = j,r = j;
 49                     while(k+1<=5&&mp[i][k]==mp[i][k+1]) k++;
 50                     while(r-1>=1&&mp[i][r]==mp[i][r-1]) r--;
 51                     if(k-r+1>=3)
 52                     {
 53                         ok = 1;
 54                         st[i][j] = 1;
 55                     }
 56                 }
 57             }
 58         if(!ok) break;
 59         for(int i=1;i<=7;i++)
 60           for(int j=1;j<=5;j++)
 61              if(st[i][j])//这里实际没有处理掉下来的情况 
 62              {
 63                  cnt[0]--;
 64                  cnt[mp[i][j]]--;
 65                  mp[i][j] = 0;
 66              }
 67     }
 68 }
 69 bool dfs(int step)
 70 {
 71     if(!step) return !cnt[0];
 72     if(!check()) return 0;
 73     memcpy(backup[step],mp,sizeof mp);
 74     memcpy(bcnt[step],cnt,sizeof cnt);
 75     for(int j=1;j<=5;j++)
 76       for(int i=1;i<=7;i++)
 77       {
 78            if(!mp[i][j]) continue;
 79            if(j+1<=5)
 80            {
 81                path[step] = {i-1,j-1,1};
 82                move(i,j,j+1);
 83                if(dfs(step-1)) return 1;
 84                memcpy(mp,backup[step],sizeof mp);
 85                memcpy(cnt,bcnt[step],sizeof cnt);
 86          }
 87          if(j-1>=1&&!mp[i][j-1])
 88          {
 89              path[step] = {i-1,j-1,-1};
 90              move(i,j,j-1);
 91              if(dfs(step-1)) return 1;
 92              memcpy(mp,backup[step],sizeof mp);
 93                memcpy(cnt,bcnt[step],sizeof cnt);
 94          }
 95       }
 96     return 0;
 97 }
 98 int main() 
 99 {
100     scanf("%d",&m);
101     for(int i=1;i<=5;i++)
102     {
103         int x,r = 0;
104         while(scanf("%d",&x)&&x)
105             mp[++r][i] = x,cnt[x]++,cnt[0]++;
106     }//cnt[0]记录要被消除的方块个数. 
107     if(dfs(m))
108        for(int i=m;i>=1;i--)
109          printf("%d %d %d\n",path[i].y,path[i].x,path[i].d);
110     else puts("-1");
111     return 0;
112 }

 

posted @ 2021-03-10 21:59  acmloser  阅读(98)  评论(0编辑  收藏  举报