B - 三角形棋盘上的博弈游戏
Time Limit: 3000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others)
柱爷有天上课无聊,于是和同桌卿学姐一起下一种奇特的棋:
棋盘如图:
在开始游戏前,棋盘上已经放好了一些边,然后柱爷先手,开始在棋盘上没有边的位置添加一条边上去
如果添加边后围成一个三角形则获得一分(对于棋盘上游戏开始前已经围好了的三角形,两个人都不得分)并且下一轮还该他!否则下一轮该另一个人。
如果两个人都以最优策略下棋,那么柱爷能赢么?
注:只算最小的三角形!(三个边围成的三角形)
如果能赢输出“WIN!”
必败输出“LOSE!”
平局输出“Draw”
(都不输出引号)
愚人王感觉这规则太抽象了,都没人赶预测柱爷能不能赢了,于是画了个图解释了下题意:
对应: 5 1 2 3 4 5 的情况,如果这是开局情况,那么此时两个人都是0分,改柱爷先下,
如果他在6的位置填一条边,那么4,5,6将围城一个小三角形,柱爷得一分。接下来还是改柱爷下棋。
如果他在18的位置填一条边,那么将无法构成三角形,柱爷不得分,接下来改卿学姐下棋。
一直这样,直到填完所有边后,游戏结束。此时分高的人获胜,分相同则平局。
Input
第一行一个正整数n,表示开始游戏前有多少条边已经被放上去了。
第二行有n个正整数,a1...an代表已经放好的边的编号。
1<=a1...an<=18
Output
一行,输出柱爷的结局。
如果能赢输出“WIN!”
必败输出“LOSE!”
平局输出“Draw”
解题思路:
用动态规划的方法考虑,用18位二进制数表示每根棍子是否放置,dp[x]即为状态为x时的最大净得分,枚举每一次添加的边的编号,利用位运算进行状态转移,对手得分看做的负分,每次维护最大值,最后求出答案;
#include<bits/stdc++.h>
using namespace std;
#define MAXN 20
#define inf 0x3f3f3f3f
int g[MAXN],m[MAXN][MAXN],dp[1<<18];//g数组表示与编号为i的边能构成三角形的边的数目(2,4),m数组表示与编号为i的边能构成三角形的边的编号;
int solve(int x)//状态为x时净得分
{
if(x==((1<<18)-1))//无法再放边,得分为零;
return 0;
if(dp[x]!=-inf)//记忆化
return dp[x];
int i,j,k,ans;
for(i=0,j=1;i<18;i++,j<<=1)
{
if((x&j)==0)
ans=0;
else//放的位置已有边
continue;
for(k=0;k<g[i];k+=2)
if((1<<(m[i][k]-1)&x)>0&&(1<<(m[i][k+1]-1)&x)>0)//判断能否构成三角形
ans++;
if(ans>0)
dp[x]=max(dp[x],ans+solve(x|j));
else
dp[x]=max(dp[x],-solve(x|j));//对手得分看做得负分
}
return dp[x];
}
void init()
{
int i,j,k;
for(i=0;i<1<<18;i++)
dp[i]=-inf;
for(i=1;i<=18;i++)
{
if(i==3||i==5||i==6||i==7||i==9||i==11||i==13||i==14||i==16)
g[i-1]=4;
else
g[i-1]=2;
}
m[0][0]=2;m[0][1]=3;//预处理打表
m[1][0]=1;m[1][1]=3;
m[2][0]=1;m[2][1]=2;m[2][2]=5;m[2][3]=7;
m[3][0]=5;m[3][1]=6;
m[4][0]=4;m[4][1]=6;m[4][2]=3;m[4][3]=7;
m[5][0]=4;m[5][1]=5;m[5][2]=11;m[5][3]=13;
m[6][0]=3;m[6][1]=5;m[6][2]=8;m[6][3]=9;
m[7][0]=7;m[7][1]=9;
m[8][0]=7;m[8][1]=8;m[8][2]=14;m[8][3]=16;
m[9][0]=11;m[9][1]=12;
m[10][0]=10;m[10][1]=12;m[10][2]=6;m[10][3]=13;
m[11][0]=10;m[11][1]=11;
m[12][0]=6;m[12][1]=11;m[12][2]=15;m[12][3]=14;
m[13][0]=13;m[13][1]=15;m[13][2]=9;m[13][3]=16;
m[14][0]=13;m[14][1]=14;
m[15][0]=14;m[15][1]=9;m[15][2]=17;m[15][3]=18;
m[16][0]=16;m[16][1]=18;
m[17][0]=16;m[17][1]=17;
}
int main()
{
init();
int n,x=0,a;
scanf("%d",&n);
while(n--)
{
scanf("%d",&a);
x|=1<<(a-1);
}
int ans=solve(x);
if(ans>0)
printf("WIN!");
else if(ans==0)
printf("Draw");
else
printf("LOSE!");
return 0;
}