2016 UESTC Training for Math Problem B-三角形棋盘上的博弈游戏(打表+DP)

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值