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

题面

这种搜索要把后继状态都跑出来之后取Min/Max

也就是回溯的时候进行操作

记得用hash进行记忆化(用map不开O2会TLE)

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<map>
#define rg register
#define max(x,y) (x)<(y)?(y):(x)
#define min(x,y) (x)>(y)?(y):(x)
using namespace std;
int n,m,A[11][11],B[11][11],ri[11];//ri[]:每行向右延伸到了多少
map<long long,int>dp;
inline int read()
{
    rg int save=0,w=1;rg char q=getchar();
    while(q<'0'||q>'9'){if(q=='-')w=-1;q=getchar();}
    while(q>='0'&&q<='9')save=(save<<3)+(save<<1)+q-'0',q=getchar();
    return save*w;
}
inline long long zip()//算出hash值
{
    rg long long hash=0;
    for(rg int i=1;i<=n;++i)hash=(hash<<3)+(hash<<1)+hash+ri[i];
    return hash;
}
inline void recover(rg long long hash)//把状态解析出来
{
    for(rg int i=n;i>=1;--i)ri[i]=hash%11,hash/=11;
}
int dfs(rg long long hash,rg bool b)//当前状态为hash,是谁下子(b==1为菲菲,b==0为牛牛)
{
    rg int ans=((!b)?2147483647:-2147483647),i;
    if(dp[hash])return ((dp[hash]==-1)?0:dp[hash]);//如果该状况已经拓展过了,把记忆化的值返回即可
    //且因为A数组与B数组相加减的答案可能为0,而dp[]需要记录答案,所以当答案为0时,赋给dp[]的值为-1
    recover(hash);
    for(i=1;i<=n;++i)//枚举在哪一行向右走
    {
        if(ri[i]<ri[i-1])
        {
            ri[i]++;
            rg int behind=dfs(zip(),!b);//回溯回来的答案为behind
            if(b)ans=max(ans,behind+A[i][ri[i]]);//菲菲尽量使差变大  (差:菲菲-牛牛)
            else ans=min(ans,behind-B[i][ri[i]]);//牛牛尽量使差变小
            ri[i]--;//回溯,试着走别的点
        }
    }
    if(ans==((!b)?2147483647:-2147483647))ans=0;
    dp[hash]=(!ans)?-1:ans;
    return ans;
}
int main()
{
    n=read(),m=read();
    rg int i,j;
    for(i=1;i<=n;++i)
        for(j=1;j<=m;++j)
            A[i][j]=read();
    for(i=1;i<=n;++i)
        for(j=1;j<=m;++j)
            B[i][j]=read();
//    Take me to your heart, take me to your soul, give me your hand and hold me.
    ri[0]=m;
    printf("%d\n",dfs(0,1));
    return 0;
}
 

 

posted @ 2018-07-28 22:51  Cwen_oier  阅读(139)  评论(0编辑  收藏  举报