• 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
    乱构造的数据,不过代码确实是错了,还为了这个数据查了半天
  • 相关阅读:
    VMware vSphere 服务器虚拟化之二十四 桌面虚拟化之手动池管理物理机
    重思人性自我修养
    初窥Linux 之 数据流重定向
    MTD设备驱动
    wubi安装ubuntu后,增加swap大小,优化swap的使用参数-----------让ubuntu健步如飞,为编译android源码准备
    【deep learning学习笔记】最近读的几个ppt(四)
    computer专业术语总结
    弃用个人博客站重返CSDN缘由
    Hadoop1.0.4伪分布式安装
    谈谈我2013上半年在公司内部培训的经历
  • 原文地址:https://www.cnblogs.com/ambition/p/Three_Kingdom_Chess.html
Copyright © 2020-2023  润新知