题目链接:点击打开链接
题意:两个人轮流在九个小正三角形组成的大三角形上面选取边,不能重复选取,如果一个玩家选取的某条边后能组成新的小三角形,则该玩家得分为新组成三角形的个数,且进行下一次选择,比赛结束后谁的分值高谁获胜。
DP[status]表示的是当前局势下,该玩家到游戏结束最多可以得到多少分(取分差绝对值~~)
题目本身不算难,麻烦的是把题目输出每条边对应到指定的压缩的状态以及判断玩家的得分~~~
(好麻烦啊,好麻烦啊,我的转化成状态的思路全部写在init()函数里面了,逃~~~)
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
//状态压缩+DP
const int inf=0x3f3f3f3f;
int dp[1<<18];
int edge[12][12];
int tri[12];
//idx是新加的边,计算新加的边能构成多少个三角形
int calculate(int status,int idx)
{
int ret=0;
for(int i=0;i<9;i++)
if(((1<<idx)&tri[i])&&((status&tri[i])==tri[i])) ret++;
return ret;
}
//预处理出所有状态,方便后续处理
void init()
{
memset(edge,-1,sizeof(edge));
edge[1][2]=0;edge[1][3]=1;edge[2][3]=2;
edge[2][4]=3;edge[2][5]=4;edge[4][5]=5;
edge[3][5]=6;edge[3][6]=7;edge[5][6]=8;
edge[4][7]=9;edge[4][8]=10;edge[7][8]=11;
edge[5][8]=12;edge[5][9]=13;edge[8][9]=14;
edge[6][9]=15;edge[6][10]=16;edge[9][10]=17;
for(int i=0;i<6;i++)
{
int status1=1<<(i*3);
int status2=1<<(i*3+1);
int status3=1<<(i*3+2);
int status=status1|status2|status3;
tri[i]=status;
}
const int rev[][3]={{2,4,6},{5,10,12},{8,13,15}};
for(int i=0;i<4;i++)
{
int status1=1<<rev[i][0];
int status2=1<<rev[i][1];
int status3=1<<rev[i][2];
int status=status1|status2|status3;
tri[i+6]=status;
}
}
//DP表示的是当前局势下,该玩家到游戏结束最多可以得到多少分
int dfs(int status)
{
if(dp[status]) return dp[status];
if(status==((1<<18)-1)) return 0;
int ret=-inf;
for(int i=0;i<18;i++)
if((status&(1<<i))==0)
{
int status2=status|(1<<i);
int cal=calculate(status2,i);
if(cal) ret=max(ret,cal+dfs(status2));
else ret=max(ret,-dfs(status2));
}
return dp[status]=ret;
}
int main()
{
int t,nofcase=1;
init();
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
int person=0,score=0,status=0;
int cal;
for(int i=0;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
status|=(1<<edge[u][v]);
cal=calculate(status,edge[u][v]);
if(cal) score+=(person==0?1:-1)*cal;
else person^=1;
}
if(person==0) score+=dfs(status);
else score-=dfs(status);
printf("Game %d: %c wins.\n",nofcase++,score>0?'A':'B');
}
return 0;
}