[BZOJ5248][2018九省联考]一双木棋

题目描述

https://www.lydsy.com/JudgeOnline/problem.php?id=5248

 

Solution

我们首先考虑放棋子的操作

发现它一定放棋子的部分是一个联通块,不放棋子的是一个联通块

所以 我们考虑怎么表示这个轮廓

分析一下 N,M<=10 ?!

状态压缩?大爆搜?

然后我选择了状压…[听说大爆搜+hash也能做x]

用一串01串表示这个轮廓 表示从右上开始的一条路径,0表示向左走一步,1表示向下走一步,这样就可以通过一串0和1表示一个轮廓

每次的操作就是把一个01[左1下1]变成一个10[下1左1]

然后就是解决博弈的问题,理性分析[感性理解]一下 用dp[st]表示轮廓线状态为st的时候 最小化得分的差值

然后再感性理解一下

因为后手在第二步时 有办法选择对自己最优的情况进行转移,所以后手的转移就从他所能到达的状态选择一个最优的状态转移过来,如果当前是先手,就从当前可以到达的状态最劣的状态转移过来[感性理解x]

因为直接转移的话层次不是很显然,这里采取记忆化搜索实现这个dp[代码细节还是挺多的x]

Code

  1. #include <bits/stdc++.h>  
  2. using namespace std;  
  3. int N,M;  
  4. bool used[5050006];  
  5. int dp[5050006],qwq;  
  6. int A[25][25],B[25][25];  
  7. int dfs(int st)  
  8. {  
  9.     if (used[st])  
  10.       return dp[st];      
  11.       int x=1,y=N+1,Con=0;  
  12.     int &ans=dp[st];  
  13.     used[st]=1;  
  14.     for (int i=0;i<qwq;i++)  
  15.       if (st&(1<<i)) Con+=y-1;  
  16.       else y--;  
  17.     y=N+1;  
  18.     ans=-1e9;  
  19.     if (st&1) x++;  
  20.     else y--;  
  21.     for (int i=1;i<qwq;i++)  
  22.     {  
  23.       if ((st&(1<<i))&&!(st&(1<<(i-1))))  
  24.          {  
  25.             if (Con%2==0)  
  26.                ans=max(-dfs(st^(1<<i)^(1<<(i-1)))+A[y][x],ans);  
  27.             else ans=max(-dfs(st^(1<<i)^(1<<(i-1)))+B[y][x],ans);  
  28.          }  
  29.       if (st&(1<<i))  
  30.         x++;  
  31.        else y--;  
  32.     }  
  33.     return ans;  
  34. }  
  35. int main()  
  36. {  
  37.     scanf("%d%d",&N,&M);  
  38.     qwq=N+M;      
  39.     used[(1<<M)-1]=1;  
  40.     for (int i=1;i<=N;i++)  
  41.       for (int j=1;j<=M;j++)  
  42.         scanf("%d",&A[i][j]);  
  43.     for (int i=1;i<=N;i++)  
  44.       for (int j=1;j<=M;j++)  
  45.         scanf("%d",&B[i][j]);  
  46.     cout<<dfs(((1<<(N+M))-1)^((1<<N)-1));  
  47.     return 0;  
  48. }  
原文地址:https://www.cnblogs.com/si--nian/p/10478995.html