转载请注明出处,谢谢http://blog.csdn.net/acm_cxlove/article/details/7854526 by---cxlove
题目:给出一个n*n的矩阵,两个依次找一个空格子,放入0和1,最终看连通0多还是连通的1多。(我的表述纯属瞎扯)
http://poj.org/problem?id=3317
题目是让当前的人有没有最优策略。
还是极大极小搜索,把空格子提取出来,由于最多10个,可以状态压缩一下。
结果这题的alpha-beta剪枝毫无作用,尝试了一下,时间没有减少。反而需要记忆化搜索。
把空的格子3进制压缩一下,否则会TLE。
由于是问当前的人的最优策略,所以可以转换一下,把当前的人转换成'0',方便一点。
不过alpha-beta剪枝是不能配合记忆化搜索的。
以下摘自dicuss:
alpha-beta剪枝使得每一个子状态在它的父亲兄弟们的约束下,得出一个相应得值,所以与其父兄节点有关系,而记忆化搜索则默认子节点只与自己的状态有关系,忽略了父兄的约束条件,实际上一颗博弈树上可能有多颗节点同时指向一个节点,若把alpha-beta与记忆化结合起来,那么该节点将只被一组父兄节点限制一次,也就只考虑了这组父兄所带来的alpha-beta界剪枝下的结果,很有可能把本属于另外组别父兄节点的最优解给误剪掉了……
这段话还希望有大神指点一下,不太懂
#include<iostream> #include<cstdio> #include<map> #include<cstring> #include<cmath> #include<vector> #include<queue> #include<algorithm> #include<set> #include<string> #define inf 1<<30 #define N 100005 #define maxn 100005 #define Min(a,b) ((a)<(b)?(a):(b)) #define Max(a,b) ((a)>(b)?(a):(b)) #define pb(a) push_back(a) #define mem(a,b) memset(a,b,sizeof(a)) #define eps 1e-9 #define zero(a) fabs(a)<eps #define LL long long #define ULL unsigned long long #define lson (step<<1) #define rson (step<<1|1) #define MOD 1000000007 #define mp(a,b) make_pair(a,b) using namespace std; struct Point{ int x,y; Point(){} Point(int _x,int _y):x(_x),y(_y){} }p[10],ansp; int n,tot,best; char str[10][10]; int vis[10][10],tt; int way[4][2]={0,1,0,-1,1,0,-1,0}; int pw3[15],dp[60000]; bool check(int x1,int y1,int x2,int y2){ if(x1>=0&&x2>=0&&x1<n&&x2<n&&str[x1][y1]==str[x2][y2]&&!vis[x1][y1]) return true; return false; } void dfs(int i,int j){ if(vis[i][j]) return; tt++;vis[i][j]=1; for(int k=0;k<4;k++){ int ii=i+way[k][0],jj=j+way[k][1]; if(check(ii,jj,i,j)) dfs(ii,jj); } } int get_score(){ mem(vis,0); int c1=0,c2=0; for(int i=0;i<n;i++) for(int j=0;j<n;j++){ tt=0; if(vis[i][j]==0) dfs(i,j); if(str[i][j]=='0') c1=max(c1,tt); else c2=max(c2,tt); } return c1-c2; } int MinSearch(int,int,int,int); int MaxSearch(int,int,int,int); int MaxSearch(int state,int dep,int now,int alpha){ //棋盘放满,统计一下当前局面 if(state==0)return get_score(); if(dp[now]!=-inf) return dp[now]; int ans=-inf,st=state; //枚举所有的位置 while(st){ int k=st&(-st),pos; for(pos=0;;pos++) if((1<<pos)&k) break; str[p[pos].x][p[pos].y]='0'; int tmp=MinSearch(state-k,dep+1,now+pw3[pos],ans); str[p[pos].x][p[pos].y]='.'; ans=max(tmp,ans); if(tmp>=alpha) return tmp; //更新一下最优解 if(dep==0){ if(ans>best||(ans==best&&(p[pos].x<ansp.x||(p[pos].x==ansp.x&&p[pos].y<ansp.y)))){ best=ans; ansp=p[pos]; } } st-=k; } return dp[now]=ans; } int MinSearch(int state,int dep,int now,int beta){ //棋盘放满,统计一下当前局面 if(state==0) return get_score(); //记忆化搜索 if(dp[now]!=-inf) return dp[now]; int ans=inf,st=state; //枚举所有的位置 while(st){ int k=st&(-st),pos; //找一下是第几个点 for(pos=0;;pos++) if((1<<pos)&k) break; //搜索 str[p[pos].x][p[pos].y]='1'; int tmp=MaxSearch(state-k,dep+1,now+2*pw3[pos],ans); str[p[pos].x][p[pos].y]='.'; ans=min(tmp,ans); //剪枝 if(ans<=beta) return ans; st-=k; } return dp[now]=ans; } int main(){ pw3[0]=1; for(int i=1;i<=10;i++) pw3[i]=pw3[i-1]*3; while(scanf("%d",&n)!=EOF&&n){ int c1=0,c2=0;tot=0; for(int i=0;i<n;i++){ scanf("%s",str[i]); for(int j=0;j<n;j++){ if(str[i][j]=='.') p[tot++]=Point(i,j); else if(str[i][j]=='0') c1++; else c2++; } } //我们都把'0'搞成先手,这样方便一点 if(c1>c2){ 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; int ret; for(int i=0;i<pw3[tot];i++) dp[i]=-inf; ret=MaxSearch((1<<tot)-1,0,0,inf); printf("(%d,%d) %d\n",ansp.x,ansp.y,best); } return 0; }