HDOJ 4083 Three Kingdom Chess【2011 ACM Asia Beijing Regional Problem C】

这两天趁着威哥不在,拿来威哥的机械键盘敲个码量略大点的题爽一下来着,可这一爽就爽了两天啊操,默泪 T_T

这题算法上来说不算是难题,如果知道极大极小搜索和Alpha_Beta剪枝的话算是比较裸的了,减枝也比较容易想到,麻烦的就是模拟的过程了。

去年北京赛后才知道有Alpha_Beta剪枝这个东西,一直拖到今年才学,北京赛的这道题又那么一身的神性,终于给过了。题目意思属于那种看了一半就想把出题人砍了的那种,很多规则:诸葛亮和周瑜下三国棋,轮流走,诸葛亮先;棋盘,有平地、山和水三种;三种类型的兵,步兵、弓箭手和骑兵;骑兵不能上山,三种兵都不能下水;每轮每个人可以选择自己的一个兵走一定步数(步数有输入),走完后可以再攻击一下,走的时候不能经过敌人,可以经过同伴但不能和同伴停在同一格,当然不能经过自己不能走的地形;攻击有范围,看图就知道了,对方的力量掉一定伤害,伤害是自己的力量*一个系数,系数按图中顺序为2,逆序0.5(向下取整),同类型打为1;好像没别的规则了,最多k次游戏结束,问你结束时最高的力量差(诸葛亮的减周瑜),当然两人都是神,一点错误都不会犯的那种。

  

方法也不用怎么想,就是极大极小的框架然后暴搜加剪枝,Alpha_Beta剪枝不用说,肯定要的;状态山大,没法记忆化就不去记忆化了,有层数限制,用处也不会大;搜索时排序剪枝也容易想到,写起来略麻烦。这么几个剪枝下来,如果没敲错,没看错输入里面和图上是刚好颠倒着的话就可以过了,嗯,应该可以过了,这题也不那么卡时间,嗯?我为什么TLE那么多次?啊啊啊啊,k&1==0里面&优先级比等号低啊,少了个括号然后几个剪枝就完全没效果了啊啊啊啊啊。还一个剪枝也容易想到,至少在调的时候就可以发现这里跑了很多没用的状态了,就是如果后面的操作已经非常完美了(你把剩下的敌人全部无伤干掉了)就返回了(不知道是写错了还是怎么的,貌似只快了100ms+)。还有一点,开始时候我让他走到一个点之后如果能打就打了,把不能打的状态删掉了,然后就一跪不起了,看下面这种吧:

##...

raF..

#####

大写的是诸葛亮的,小写的是周瑜的,#是水,比如说F都是力量超高的,r比F还要高的力量,a是弱菜:这时候r出不去也打不着,F要能挨啊,如果把a打死了,r被放出来,后果不堪设想啊。当然对r来说,那什么,猪一样的同伴...

虽然说麻烦程度胜似模拟了吧,不过错这么多次是要闹哪样啊,代码能力跪舔到极限了啊,码跑的不快,倒数,再不想碰了:

  1  #include<cstdio>
  2  #include<cstring>
  3  #include<algorithm>
  4  #include<cstdlib>
  5  using namespace std;
  6  
  7  int ratk[12][2]={
  8          {-1,0},{0,-1},{0,1},{1,0},
  9          {-1,-1},{-1,1},{1,-1},{1,1},
 10          {-2,0},{0,-2},{0,2},{2,0}
 11  };
 12  int natk[3]={8,12,4};
 13  double fatk[3][3]={{1,2,0.5},{0.5,1,2},{2,0.5,1}};
 14  int dir[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
 15  
 16  struct Soldier{
 17      int flag, type;
 18      Soldier(){}
 19      Soldier(int f, int t):flag(f), type(t){}
 20  }sld[8];
 21  
 22  struct Next{
 23      int id, mx, my, aid, sc;
 24  
 25      Next(){}
 26      Next(int _id, int _mx, int _my, int _aid, int _sc):
 27          id(_id), mx(_mx), my(_my), aid(_aid), sc(_sc){}
 28  
 29      friend bool operator< (const Next& a, const Next& b){
 30          return a.sc > b.sc;
 31      }
 32  }nexts[12][150]; //每层到下一层的走法,这里都开好了不用递归时临时开了 
 33  int nsz[12];
 34  
 35  struct State{
 36      int x, y, sc; 
 37      State(){}
 38      State(int _x, int _y, int _sc):
 39          x(_x), y(_y), sc(_sc){} 
 40  }sta[12][8]; 
 41  short smaze[12][8][8]; //递归最多10层,每层一个状态就不用临时开了 
 42  
 43  int lx, ly, lk;
 44  int n, s[3];
 45  short maze[8][8];
 46  
 47  int skm, szy;
 48  
 49  inline bool myturn(int k, int id){ //判断是不是轮到他一方的走 
 50      if(sld[id].flag!=(k&1)) return false;
 51      if(sta[k][id].sc==0) return false;
 52      return true;
 53  }
 54  
 55  inline bool moveillegal(int k, int id, int x, int y){ //移动位置是否无效 
 56      if(x<0||x>=lx) return true;
 57      if(y<0||y>=ly) return true;
 58      if(maze[x][y]==2) return true;
 59      if(sld[id].type==2&&maze[x][y]==1) return true; 
 60      if(smaze[k][x][y]!=-1&&sld[smaze[k][x][y]].flag!=sld[id].flag) return true;
 61      return false; 
 62  } 
 63  
 64  inline bool attackillegal(int k, int id, int x, int y){ //攻击位置是否无效 
 65      if(x<0||x>=lx) return true;
 66      if(y<0||y>=ly) return true;
 67      if(smaze[k][x][y]==-1) return true;
 68      if(sld[smaze[k][x][y]].flag==sld[id].flag) return true;
 69  
 70      if(sta[k][smaze[k][x][y]].sc==0) return true;
 71      return false;
 72  }
 73  
 74  inline void donext(int k, Next& p){ //修改状态 
 75      if(p.id==-1) return; //没走也没攻击 , 就不修改了 
 76      swap(smaze[k][sta[k][p.id].x][sta[k][p.id].y],smaze[k][p.mx][p.my]); //修改走法 
 77      sta[k][p.id].x = p.mx;
 78      sta[k][p.id].y = p.my;
 79  
 80      if(p.aid==-1) return;
 81      sta[k][p.aid].sc-=p.sc; //修改攻击状态
 82      sld[p.aid].flag?szy-=p.sc:skm-=p.sc;
 83      if(sta[k][p.aid].sc==0){
 84          smaze[k][sta[k][p.aid].x][sta[k][p.aid].y]=-1;
 85      } 
 86  } 
 87  
 88  inline void undonext(int k, Next& p){ //恢复状态 
 89      if(p.id==-1) return; //没走也没攻击
 90      sta[k][p.id].x = sta[k-1][p.id].x; //恢复走法 
 91      sta[k][p.id].y = sta[k-1][p.id].y;
 92      swap(smaze[k][sta[k][p.id].x][sta[k][p.id].y],smaze[k][p.mx][p.my]);
 93  
 94      if(p.aid==-1) return;
 95      sta[k][p.aid].sc+=p.sc; //恢复攻击
 96      sld[p.aid].flag?szy+=p.sc:skm+=p.sc;
 97      smaze[k][sta[k][p.aid].x][sta[k][p.aid].y]=p.aid; 
 98  } 
 99  
100  inline int attack(int k, int sid, int tid){ //计算攻击血量 
101      return min(sta[k][tid].sc,
102              int(sta[k][sid].sc*fatk[sld[sid].type][sld[tid].type]));
103  }
104  
105  inline void getattack(int k, int id, int x, int y){ //枚举一个移动的攻击,并把移动+攻击的方法塞到Next数组里 
106      for(int d=0;d<natk[sld[id].type];d++){
107          int tx = x + ratk[d][0];
108          int ty = y + ratk[d][1];
109          if(!attackillegal(k, id, tx, ty)){
110              int atkid = smaze[k][tx][ty]; 
111              int atksc = attack(k, id, atkid); 
112              nexts[k][nsz[k]++] = Next(id, x, y, atkid, atksc); 
113          }
114      } 
115  } 
116  
117  bool reach[8][8];
118  inline void stepbystep(int dep, int k, int id, int x, int y){ //递归标记可移动到的位置 
119      reach[x][y] = true;
120      if(dep==0) return;
121      for(int d=0;d<4;d++){
122          int tx = x+dir[d][0];
123          int ty = y+dir[d][1];
124          if(!moveillegal(k, id, tx, ty)){
125              stepbystep(dep-1, k, id, tx, ty);
126          }
127      }
128  }
129  
130  inline void getmove(int k, int id){ //枚举走法
131      memset(reach, false, sizeof(reach));
132      stepbystep(s[sld[id].type], k, id, sta[k][id].x, sta[k][id].y);
133      for(int i=0;i<lx;i++){
134          for(int j=0;j<ly;j++){
135              if(reach[i][j]&&smaze[k][i][j]==-1){
136                  getattack(k, id, i, j); //对每一个走法去枚举攻击方法  
137                  nexts[k][nsz[k]++] = Next(id, i, j, -1, 0); //不进行攻击的走法 
138              }
139          }
140      }
141      getattack(k, id, sta[k][id].x, sta[k][id].y); //不移动的攻击方法 
142  }
143  
144  inline void getnext(int k){ //得到下一步的所有状态 
145      nsz[k]=0;
146      for(int i=0;i<n;i++){
147          if(myturn(k,i)) getmove(k, i); //对一个棋子枚举走法 
148      }
149      nexts[k][nsz[k]++] = Next(-1, -1, -1, -1, 0); //所有棋子都不移动也不攻击 
150      sort(nexts[k], nexts[k]+nsz[k]); //按攻击血量排序 
151  }
152  
153  void debug(int k){ //调试用的 
154      for(int i=0;i<lx;i++){
155          for(int j=0;j<ly;j++){
156              if(~smaze[k][i][j]){
157                  if(sld[smaze[k][i][j]].flag==0){ 
158                      switch(sld[smaze[k][i][j]].type){
159                      case 0: putchar('F'); break; 
160                      case 1: putchar('A'); break; 
161                      case 2: putchar('R'); break; 
162                      }
163                  } else if(sld[smaze[k][i][j]].flag==1){
164                      switch(sld[smaze[k][i][j]].type){
165                      case 0: putchar('f'); break; 
166                      case 1: putchar('a'); break; 
167                      case 2: putchar('r'); break; 
168                      }
169                  } 
170              } else {
171                  switch(maze[i][j]){
172                  case 0: putchar('.'); break;
173                  case 1: putchar('*'); break;
174                  case 2: putchar('#'); break; 
175                  } 
176              }
177          } puts("");
178      } 
179      printf("%d %d\n", skm, szy);
180      getchar(); 
181  } 
182  
183  void shownext(int k, int ti){ //调试用的 
184      for(int i=0;i<nsz[k];i++){
185          if(ti==i) printf("%d>", k); 
186          Next &u = nexts[k][i];
187          printf("%d mov: %d %d, atk: %d %d\n", u.id, u.mx, u.my, u.aid, u.sc);
188      }
189  }
190  
191  int dfs(int k, int sc, int maxcut, int mincut){
192      //debug(k); 
193  
194      if(k==lk) return sc;  //k步了 
195      if(szy==0||skm==0) return sc; //一个人死光了 
196  
197      getnext(k); //得到下一步走法 
198      memcpy(sta[k+1],sta[k],sizeof(sta[k]));
199      memcpy(smaze[k+1],smaze[k],sizeof(smaze[k])); //把上一层状态复制过来 
200      int maxval = sc-szy, minval = sc+skm;
201      for(int i=0;i<nsz[k];i++){
202          //shownext(k, i); 
203          Next &u = nexts[k][i];
204          donext(k+1, u);  //递归过程 
205          int val = dfs(k+1,(k&1)?(sc-u.sc):(sc+u.sc), maxval, minval);
206          undonext(k+1, u); 
207          if(maxval<val) maxval = val;
208          if(minval>val) minval = val;
209          if(((k&1)==0)&&val>=mincut) return val; //Alpha_Beta剪枝 
210          if(((k&1)==0)&&(val-sc>=szy)) return val; //已经是完美走法的剪枝 
211          if(((k&1)==1)&&val<=maxcut) return val; //Alpha_Beta剪枝 
212          if(((k&1)==1)&&(val-sc<=-skm)) return val; //已经是完美走法的剪枝 
213      }
214      return (k&1)?minval:maxval; //极大极小过程 
215  }
216  
217  int main(){
218      while(scanf("%d%d%d", &lx, &ly, &lk),lx||ly){
219          memset(maze, 0, sizeof(maze));
220          memset(smaze, -1, sizeof(smaze));
221          for(int i=0;i<lx;i++){
222              for(int j=0;j<ly;j++){
223                  scanf("%d", &maze[i][j]);
224              }
225          }
226          scanf("%d%d%d%d", &n, &s[0], &s[2], &s[1]);
227          szy=skm=0;
228          for(int i=0;i<n;i++){
229              int x, y, a, b, c; 
230              scanf("%d%d%d%d%d", &x, &y, &a, &b, &c);
231              if(c==1) c=2; else if(c==2) c=1; //
232              x--; y--;
233              sld[i] = Soldier(a, c);
234              smaze[0][x][y] = i;
235              sta[0][i] = State(x, y, b);
236              a?szy+=b:skm+=b;
237          }
238          int ans = dfs(0, skm-szy, -szy, skm);
239          printf("%d\n", ans);
240      }
241  }

ps:晒代码里面那个好像是标称,不过有bug,找了一组数据

3 3 6
0 0 0
0 2 0 
0 0 0
5 2 1 1
2 1 1 116 1
2 3 0 125 1
1 1 1 134 2
3 1 0 143 0
3 3 1 161 2
乱构造的数据,不过代码确实是错了,还为了这个数据查了半天
posted @ 2012-07-22 21:47  Amb@HDU  阅读(960)  评论(0编辑  收藏  举报