poj 3317 Stake Your Claim(极大极小搜索经典 a-b+剪枝+记忆化dp)

题目链接

Stake Your Claim

分析

哇这题也算把我卡得够死了,怪我没能正确理解所谓a-b剪枝的姿势.
其实alpha-beta剪枝不用也是可以过的,非得我不用才找到了错误

首先这道题只有10种选择,每种选择有3种状态,共 310<60000 ,因此直接记忆话搜就行了.用3进制状压一下.然后就是枚举空点的时候也可以状压一下这样枚举快.

AC code

//Source Code

//Problem: 3317     User: zouzhitao
//Memory: 852K      Time: 94MS
//Language: G++     Result: Accepted
Source Code
#include 
#include 
#include 
#include 
#define pb push_back
#define mp make_pair
#define PI acos(-1)
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define INF64 0x3f3f3f3f3f3f3f3f
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define ms(x,v) memset((x),(v),sizeof(x))
#define scint(x) scanf("%d",&x );
#define scf(x) scanf("%lf",&x );
#define eps 1e-10
#define dcmp(x) (fabs(x) < eps? 0:((x) <0?-1:1))
#define SQ(x) (x)*(x)
using namespace std;
typedef long long LL;
typedef long double DB;
typedef pair<int,int> Pair;
const int maxn = 11;
int n;
int a[maxn][maxn];
std::vector empty;
 char s[maxn];
int vis[maxn][maxn];
int dx[] = {-1,1,0,0};
int dy[] = {0,0,1,-1};

bool bound(int x,int y){
    return (x)>=0 && (x)=0 && (y)int dfs(int x,int y) {
    vis[x][y] = 1;
    int ret = 1;
    for(int i=0 ; i<4 ; ++i){
        int nx = x+dx[i];
        int ny = y+dy[i];
        if(bound(nx,ny)&&!vis[nx][ny] && a[x][y] == a[nx][ny])ret += dfs(nx,ny);
    }
    return ret;
}

int reward(int val){
    int s1 = 0;
    ms(vis,0);
    for(int i=0 ; ifor(int j=0 ; jif(!vis[i][j] && a[i][j] == val){
                s1 = max(s1 , dfs(i,j));
            }
    }
    return s1;
 }
int ans =-INF;
 Pair first;
int dp[60000];
int three[maxn];
int minmax(int state,int player,int cur,int alpha,int beta){
    if(!state){
        int s0 = reward(0);
        int s1 = reward(1);
        return s0 - s1;
    }
    int st = state;
    if(dp[cur]!=INF)return dp[cur];
    if(!player){//先手
      alpha = -INF;
        while (st) {
            int i = __builtin_ctz(st);

            int x = empty[i].fi,y = empty[i].se;
            a[x][y] = player;
            int val = minmax(state^(1<1,cur+three[i],alpha,beta);//3进制表示选i号格子为0
            a[x][y] = -1;
            if(val > alpha)alpha = val;
            if(state ==(1<1){
                if(ans  empty[i])){
                    ans = alpha;
                    first = empty[i];
                //  std::cout << first.fi <<" " << first.se << '\n';
                }
            }
            if(alpha >=beta){
                return alpha;
            }
            st^=(1<return dp[cur] =alpha;
    }else{
      beta = INF;
        while (st) {
            int i = __builtin_ctz(st);
            int x = empty[i].fi,y = empty[i].se;
            a[x][y] = player;
            int val = minmax(state^(1<1,cur+2*three[i],alpha,beta);
            a[x][y] = -1;
            if(val if(alpha >=beta){
                return beta;
            }
            st^=(1<return dp[cur] = beta;
    }
}


int main()
{
    // ios_base::sync_with_stdio(0);
    // cin.tie(0);
    // cout.tie(0);
    three[0] =1;
    for(int i=1 ; i<=11 ; ++i)three[i] = 3*three[i-1];
    while (scanf("%d", &n) && n) {
        empty.clear();
        int n1 = 0,n2 =0;
        for(int i=0 ; iscanf("%s", s);
            for(int j=0 ; jif(s[j]=='.'){
                    empty.pb(mp(i,j));a[i][j] = -1;
                }else a[i][j] = s[j] - '0';
                n1 += (a[i][j] == 0);
                n2 += (a[i][j] ==1);
            }
        }
        if(n1 > n2){
            for(int i=0 ; ifor(int j=0 ; jif(a[i][j] ==0)a[i][j] =1;
                    else if(a[i][j] ==1)a[i][j] =0;
        }
        ms(dp,INF);
        ans = -INF;
        minmax((1<1,0,0,-INF,INF);
        printf("(%d,%d) %d\n",first.fi,first.se,ans );
    }
    //std::cout << "time "<< clock()/1000 <<"ms"<< '\n';
    return 0;
}

alpha-beta 剪枝误区

  1. 剪枝的时候不能直接将剪掉的那部分直接赋值给dp的,原因这位仁兄说的很清楚http://poj.org/showmessage?message_id=131554 dp是父亲节点与子孙节点的关系,而alpha-beta剪枝还考虑了父亲兄弟节点,,,
  2. alpha-beta剪枝需要初始化,因为是针对当前节点而言的.

你可能感兴趣的:(算法刷题)