POJ - 3317 Stake Your Claim 极大极小搜索+alpha-beta剪枝+记忆化搜索

题目:给出一个n*n包含0或1或.的矩阵,两个依次找一个空格子,放入0和1,最终看连通0多还是连通的1多,得分是一个选手的最大连通数减去另一个选手的。问当前局面的操作者能够得的最大的分数,及最大分数下最小字典序的操作。

思路:有的说alpha-beta剪枝和记忆化搜索不能一起使用,有的说只要把参数记下来就可以一起用  疑问

代码:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<algorithm>
#include<ctime>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<list>
#include<numeric>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define INF 0x3f3f3f3f
#define mm(a,b) memset(a,b,sizeof(a))
#define PP puts("*********************");
template<class T> T f_abs(T a){ return a > 0 ? a : -a; }
template<class T> T gcd(T a, T b){ return b ? gcd(b, a%b) : a; }
template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
// 0x3f3f3f3f3f3f3f3f
//0x3f3f3f3f

int dx[]={-1,1,0,0};
int dy[]={0,0,-1,1};
struct Node{
    int x,y;
    Node(int _x=0,int _y=0):x(_x),y(_y){}
}p[15],ansp;
int n,tot,best;
char str[10][10];
int vis[10][10],num;
int p3[15],dp[60000];
bool judge(int x1,int y1,int x2,int y2){
    if(x2>=0&&x2<n&&y2>=0&&y2<n&&str[x1][y1]==str[x2][y2]&&!vis[x2][y2])
        return true;
    return false;
}
void dfs(int x,int y){
    if(vis[x][y]) return;
    vis[x][y]=1;
    num++;
    for(int i=0;i<4;i++){
        int nx=x+dx[i];
        int ny=y+dy[i];
        if(judge(x,y,nx,ny))
            dfs(nx,ny);
    }
}
int get_score(){
    mm(vis,0);
    int c1=0,c2=0;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++){
            if(vis[i][j]) continue;
            num=0;
            dfs(i,j);
            if(str[i][j]=='0') c1=max(c1,num);
            else c2=max(c2,num);
        }
    return c1-c2;
}
int MaxSearch(int status,int dep,int now,int alpha);
int MinSearch(int status,int dep,int now,int beta);
int MinSearch(int status,int dep,int now,int beta){
    if(status==0) return get_score();
    if(dp[now]!=-INF) return dp[now];
    int ans=INF,remain=status;
    while(remain){
        int seg=remain&(-remain);
        int k=0;
        for(k=0;;k++)
            if((1<<k)&seg)
                break;
        str[p[k].x][p[k].y]='1';
        int tmp=MaxSearch(status-seg,dep+1,now+2*p3[k],ans);
        str[p[k].x][p[k].y]='.';
        if(ans>tmp) ans=tmp;
        if(ans<=beta) return ans;
        remain-=seg;
    }
    return dp[now]=ans;
}
int MaxSearch(int status,int dep,int now,int alpha){
    if(status==0) return get_score();
    if(dp[now]!=-INF) return dp[now];
    int ans=-INF,remain=status;
    while(remain){
        int seg=remain&(-remain);
        int k=0;
        for(k=0;;k++)
            if((1<<k)&seg)
                break;
        str[p[k].x][p[k].y]='0';
        int tmp=MinSearch(status-seg,dep+1,now+p3[k],ans);
        str[p[k].x][p[k].y]='.';
        if(ans<tmp) ans=tmp;
        if(ans>=alpha) return ans;
        if(dep==0){
            if(ans>best||(ans==best&&(p[k].x<ansp.x||(p[k].x==ansp.x&&p[k].y<ansp.y)))){
                best=ans;
                ansp=p[k];
            }
        }
        remain-=seg;
    }
    return dp[now]=ans;
}
int main(){

//    freopen("D:\\input.txt","r",stdin);
//    freopen("D:\\output.txt","w",stdout);
    p3[0]=1;
    for(int i=1;i<=10;i++) p3[i]=3*p3[i-1];
    while(~scanf("%d",&n)){
        if(!n) break;
        int c1=0,c2=0;
        tot=0;
        for(int i=0;i<n;i++)
            scanf("%s",str[i]);
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++){
                if(str[i][j]=='.') p[tot++]=Node(i,j);
                else if(str[i][j]=='0') c1++;
                else c2++;
            }
        if(c1>c2){//把'0'搞成先手
            for(int i=0;i<n;i++)
                for(int j=0;j<n;j++)
                    if(str[i][j]=='0') str[i][j]='1';
                    else if(str[i][j]=='1') str[i][j]='0';
        }
        best=-INF;
        for(int i=0;i<=p3[tot];i++) dp[i]=-INF;
        MaxSearch((1<<tot)-1,0,0,INF);
        printf("(%d,%d) %d\n",ansp.x,ansp.y,best);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值