对抗搜索/dp-hdu-4753-Fishhead’s Little Game

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4753

题目意思:

在3*3的方格中,有4*4=16个点,标号分别为1~16,A、B两人轮流玩游戏,每次可以添加一条边(相邻节点),如果恰好能够凑成一个边长为1的正方形则得一分,两个的话得2分。给定两人放的n边后,求最终格局谁会胜。

解题思路:

状态压缩+对抗搜索。(对抗搜索做少了)

和今年通话邀请赛这题很像:http://blog.csdn.net/cc_again/article/details/10284769

除去给定的n条边,还剩下不到12条边S,所以可以状态压缩,dp[i]表示,S中放了边的状态为i的情况下,先走还能获得的最大分数。

每次dfs维护一个剩余的分数sum,表示当前状态下能获得的最大分数,dp[s]=max(sum-走一步后对手获得的最大分数);

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#define eps 1e-6
#define INF 0x3fffffff
#define PI acos(-1.0)
#define ll __int64
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

int dp[1<<12];//dp[i]表示走了i状态的边,还能获得的最大分数
bool hav[20][20];
int n;

bool iscan(int aa,int bb,int b,int a) //能否围城正方形
{
    if(hav[aa][bb]&&hav[bb][b]&&hav[b][a]&&hav[a][aa])
        return true;
    return false;
}

struct Edge
{
    int x,y;
};
vector<Edge>myv;

int ok1(int a,int b)//横着的情况
{

    int aa=a-4,bb=b-4,res=0;

    if(aa>=0&&bb>=0) //上
    {
        if(iscan(aa,bb,b,a))
            res++;
    }
    aa=a+4,bb=b+4;
    if(aa<16&&bb<16) //下
    {
        if(iscan(aa,bb,b,a))
            res++;
    }
    return res;
}
int ok2(int a,int b)
{
    int res=0;
    int aa,bb;

    aa=a-1,bb=b-1;
    if(bb%4!=3) //aa可能为负数 左
    {
        if(iscan(a,b,bb,aa))
            res++;
    }
    aa=a+1,bb=b+1; //右
    if(aa%4)
    {
        if(iscan(a,b,bb,aa))
            res++;
    }
    return res;
}
int dfs(int ord,int s,int sum)
{
    if(ord==25||sum<=0) //分数已经得完了
        return 0;
    if(dp[s]!=-1) //记忆话搜索
        return dp[s];
    int res=0;
    for(int i=0;i<myv.size();i++)
    {
        if((1<<i)&s)
            continue;
        hav[myv[i].x][myv[i].y]=true;
        hav[myv[i].y][myv[i].x]=true;
        if(myv[i].x==myv[i].y-1) //行
            res=max(res,sum-dfs(ord+1,s|(1<<i),sum-ok1(myv[i].x,myv[i].y)));
        else
            res=max(res,sum-dfs(ord+1,s|(1<<i),sum-ok2(myv[i].x,myv[i].y)));
        hav[myv[i].x][myv[i].y]=false;
        hav[myv[i].y][myv[i].x]=false;
    }
    dp[s]=res;
    return res;
}
int main()
{
   int t;

   scanf("%d",&t);
   for(int ca=1;ca<=t;ca++)
   {
       scanf("%d",&n);
       memset(hav,false,sizeof(hav));
       myv.clear();
       int pa=0,pb=0,a,b;

       for(int i=1;i<=n;i++)
       {
           scanf("%d%d",&a,&b);
           a--,b--;
           hav[a][b]=hav[b][a]=true;
           if(a>b)
            swap(a,b);
           if(a==b-1) //在同一行
           {
               if(i&1)
                    pa+=ok1(a,b);
               else
                    pb+=ok1(a,b);
           }
           else//同一列
           {
               if(i&1)
                    pa+=ok2(a,b);
               else
                    pb+=ok2(a,b);
           }
       }
       for(int i=0;i<16;i++) //统计剩下的边
            for(int j=i+1;j<16;j++)
            {
                if(hav[i][j])
                    continue;
                if(i==j-1&&j%4)
                {
                    Edge p;
                    p.x=i,p.y=j;
                    myv.push_back(p);
                }
                if(i==j-4)
                {
                    Edge p;
                    p.x=i,p.y=j;
                    myv.push_back(p);
                }
            }
        //printf("%d %d\n",pa,pb);
        memset(dp,-1,sizeof(dp));
        if(n<24)
        {
            if((n+1)&1) //当前谁先走
            {
                pa+=dfs(n+1,0,9-pa-pb);
                pb=9-pa;
            }
            else
            {
                pb+=dfs(n+1,0,9-pa-pb);
                pa=9-pb;
            }
        }
        //printf("ljdj\n");
        //printf("%d %d\n",pa,pb);
        printf("Case #%d: %s\n",ca,pa>=5?"Tom200":"Jerry404");
   }
   return 0;
}


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值