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

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

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

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

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

这题算法上来说不算是难题,如果知道极大极小搜索和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
乱构造的数据,不过代码确实是错了,还为了这个数据查了半天

相关文章: