题目链接:
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;
}