【HDU5897 2016 ACM ICPC Asia Regional Shenyang Online F】【模拟+有向图博弈】The Game 双将一马最长获胜时间.cpp

The Game

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 46    Accepted Submission(s): 9


Problem Description
Alice and Bob like playing Chinese Chess. One day they are playing a game: Alice has a   General, Bob has a   General  and a   Horse, they both obey the rules of Chinese Chess, which are:

  General  has 4 moving directions,   Horse  has 8 moving directions, but they can't move out of the chess board or their own moving areas. See the picture to get more details:



 In some circumstances,   Horse  can't move to all his positions, just as you can see in the picture, it can move to the black positions, but can't move to the red positions:



 Two   Generals can eat each other if and only if they have the same y-coordinate and   Horse  is not between them.
(For more information, click   http://https://en.wikipedia.org/wiki/Xiangqi)

One is considered to be the winner only when his enemy's   General  is eaten. Please note that if the game is a tie, Alice is considered to be the winner. If Alice know that she has no chance to win the game at the beginning, she will take the strategy to make the game last more steps. If Alice can't win, Bob want to end the game as soon as possible.

If Alice win the game, output "Lucky guy!", else output "Lose in x steps T.T!", x indicates the number of steps they take (the sum of Alice's and Bob's steps). Suppose they all make best strategies.
 

Input
7 numbers   x1,y1,x2,y2,x3,y3,p, where:

  (x1,y1)indicates the coordinate of the Bob's   Horse   (0x19,0y18);  
  (x2,y2)indicates the coordinate of the Bob's   General   (7x29,3y25);
  (x3,y3)indicates the coordinate of the Alice's   General   (0x33,3y35);

  p  indicates the people who moves first, 0 for Alice, 1 for Bob.
 

Output
If Alice win, output "Lucky guy!" (without quotations), else output “Lose in x steps T.T!”, x indicates the number of steps they take (the sum of Alice's and Bob's steps).
 

Sample Input
 
   
5 0 0 9 3 0 3 0 2 6 9 5 2 3 1 0 4 7 3 1 4 0 3 2 7 4 1 3 0 3 2 7 4 1 3 1
 

Sample Output
 
   
Lucky guy! Lose in 3 steps T.T! Lucky guy! Lose in 4 steps T.T! Lose in 1 steps T.T!
Hint
In sample 1, Alice moves first, she can eat Bob's General directly; In sample 2, Bob move his Horse to $(0,5)$, then wherever Alice move her General, it will be eaten by Bob's Horse, so they take 3 steps in total.
 

Source
2016 ACM/ICPC Asia Regional Shenyang Online

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template inline void gmin(T1 &a, T2 b) { if (binline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
const int dy1[8] = { -2,-2,-1,-1,1,1,2,2 };
const int dx1[8] = { -1,1,-2,2,-2,2,-1,1 };
const int dy2[8] = { -1,-1,-1,-1,1,1,1,1 };
const int dx2[8] = { -1,1,-1,1,-1,1,-1,1 };
const int dy3[4] = { -1,0,0,1 };
const int dx3[4] = { 0,-1,1,0 };
int f[10][9][10][9][10][9][2];
int out[10][9][10][9][10][9];
struct Q
{
	int y1, x1, y2, x2, y3, x3, p;
}; queueq;
//检测马的位置是否合法
bool ok1(int y, int x)
{
	return y >= 0 && y <= 9 && x >= 0 && x <= 8;
}
//检测Bob的将位置是否合法
bool ok2(int y, int x)
{
	return y >= 7 && y <= 9 && x >= 3 && x <= 5;
}
//检测Alice的将位置是否合法
bool ok3(int y, int x)
{
	return y >= 0 && y <= 2 && x >= 3 && x <= 5;
}
//判断两个点是否相同
bool dif(int y1, int x1, int y2, int x2)
{
	return y1 != y2 || x1 != x2;
}
//判定一个状态点是否合法
bool ok(int y1, int x1, int y2, int x2, int y3, int x3)
{
	return ok1(y1, x1) && ok2(y2, x2) && ok3(y3, x3)
		&& dif(y1, x1, y2, x2) && dif(y1, x1, y3, x3) && dif(y2, x2, y3, x3);
}
//按照原则来讲,为了使得拓扑度数out[]正确发挥功能,每个必败态一定要保证只能入队一次。
void failit(int y1, int x1, int y2, int x2, int y3, int x3, int p, int step)
{
	if (!ok(y1,x1,y2,x2,y3,x3))return;
	if (~f[y1][x1][y2][x2][y3][x3][p])return;
	f[y1][x1][y2][x2][y3][x3][p] = step;
	q.push({ y1,x1,y2,x2,y3,x3,p });
}
void winit(int y1, int x1, int y2, int x2, int y3, int x3, int p)
{
	if (!ok(y1,x1,y2,x2,y3,x3))return;
	f[y1][x1][y2][x2][y3][x3][p] = 0;
}
bool attack1(int y1, int x1, int y3, int x3)
{
	int y = abs(y1 - y3);
	int x = abs(x1 - x3);
	return y == 2 && x == 1 || y == 1 && x == 2;
}
bool attack2(int y1, int x1, int y3, int x3)
{
	int y = abs(y1 - y3);
	int x = abs(x1 - x3);
	//注意对优先级的判断和括号的使用
	return (y == 0 && x == 1 || y == 1 && x == 0) && ok3(y1, x1);
}
//把Alice的初始必胜态和必胜态求出,去掉这些态之后,棋盘中一定随时都是三棋共存的状态
void getbegin()
{
	for (int y1 = 0; y1 <= 9; ++y1)
	{
		for (int x1 = 0; x1 <= 8; ++x1)
		{
			for (int y2 = 7; y2 <= 9; ++y2)
			{
				for (int x2 = 3; x2 <= 5; ++x2)
				{
					for (int y3 = 0; y3 <= 2; ++y3)
					{
						for (int x3 = 3; x3 <= 5; ++x3)
						{
							if (!ok(y1,x1,y2,x2,y3,x3))continue;
							
							//如果两将共线,且马不在中间,会导致两种直接的胜负关系,不会遇到吃马和被马吃的情况
							if (x3 == x2)
							{
								if (x1 != x2 || y1 > y2 || y1 < y3)
								{
									failit(y1, x1, y2, x2, y3, x3, 1, 1);
									winit(y1, x1, y2, x2, y3, x3, 0);
								}
							}
							//如果两将不共线,那可能有被马吃和吃马两种胜负关系,(注意,吃马的条件还要求马不被敌方将保护)
							else
							{
								if (attack1(y1, x1, y3, x3))
								{
									failit(y1, x1, y2, x2, y3, x3, 1, 1);
								}
								if (attack2(y1, x1, y3, x3) && x1 != x2)
								{
									winit(y1, x1, y2, x2, y3, x3, 0);
								}
							}
						}
					}
				}
			}
		}
	}
}
//我们对胜负态做转移的时候,因为是个拓扑bfs,所以Bob只要在第一次有必败态后继就会走,而Alice会在最后一个必败态后继出来时才会走
//于是为了我们知道Alice在什么时候走动,对Alice的可行走位置做统计
void getout()
{
	for (int y1 = 0; y1 <= 9; ++y1)
	{
		for (int x1 = 0; x1 <= 8; ++x1)
		{
			for (int y2 = 7; y2 <= 9; ++y2)
			{
				for (int x2 = 3; x2 <= 5; ++x2)
				{
					for (int y3 = 0; y3 <= 2; ++y3)
					{
						for (int x3 = 3; x3 <= 5; ++x3)
						{
							if (!ok(y1,x1,y2,x2,y3,x3))continue;
							//此时轮到Alice走,操作(y3,x3)
							for (int k = 0; k < 4; ++k)
							{
								int yy = y3 + dy3[k];
								int xx = x3 + dx3[k];
								if (!ok(y1,x1,y2,x2,yy,xx))continue;
								++out[y1][x1][y2][x2][y3][x3];
							}
						}
					}
				}
			}
		}
	}
}
void go()
{
	while (!q.empty())
	{
		int y1 = q.front().y1;
		int x1 = q.front().x1;
		int y2 = q.front().y2;
		int x2 = q.front().x2;
		int y3 = q.front().y3;
		int x3 = q.front().x3;
		int p = q.front().p;
		q.pop();
		int now = f[y1][x1][y2][x2][y3][x3][p];
		if (p == 0)//当前这步轮到Alice走,上一步(转移步)轮到Bob走
		{
			//Bob会选择一个步数最少的前驱
			//一.走马
			for (int k = 0; k < 8; ++k)
			{
				int yy = y1 + dy1[k];
				int xx = x1 + dx1[k];
				int yyy = y1 + dy2[k];
				int xxx = x1 + dx2[k];
				//注意判定其前驱步走向其的时候不能被格马蹄
				if (ok(yy,xx,y2,x2,y3,x3) && dif(yyy, xxx, y2, x2) && dif(yyy, xxx, y3, x3))
				{
					failit(yy, xx, y2, x2, y3, x3, p ^ 1, now + 1);
				}
			}
			//二.走将
			for (int k = 0; k < 4; ++k)
			{
				int yy = y2 + dy3[k];
				int xx = x2 + dx3[k];
				if (ok(y1,x1,yy,xx,y3,x3))
				{
					failit(y1, x1, yy, xx, y3, x3, p ^ 1, now + 1);
				}
			}
		}
		else//当前这步轮到Bob走,上一步(转移步)轮到Alice走
		{
			//Alice会选择一个步数最长的前驱
			for (int k = 0; k < 4; ++k)
			{
				int yy = y3 + dy3[k];
				int xx = x3 + dx3[k];
				if (ok(y1,x1,y2,x2,yy,xx))
				{
					if (--out[y1][x1][y2][x2][yy][xx] == 0)
					{
						failit(y1, x1, y2, x2, yy, xx, p ^ 1, now + 1);
					}
				}
			}
		}
	}
}
//我们为什么要把f的值设置为-1呢?其实只是为了方便标记不确定态啦!
void init()
{
	MS(f, -1);
	getbegin();
	getout();
	go();
}
int main()
{
	//fre();
	init();
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		int y1, x1, y2, x2, y3, x3, p;
		scanf("%d%d%d%d%d%d%d", &y1, &x1, &y2, &x2, &y3, &x3, &p);
		if (!ok(y1,x1,y2,x2,y3,x3))while (1);//检测表示初始数据一定合法
		if (f[y1][x1][y2][x2][y3][x3][p] == 0)puts("Lucky guy!");
		else printf("Lose in %d steps T.T!\n", f[y1][x1][y2][x2][y3][x3][p]);
	}
	return 0;
}
/*
【trick&&吐槽】

有一个情况是我们需要处理的。
就是如果两个棋子走到相同的点,算什么?
其实这种情况一定不会发生。因为所有直接胜负的状态都被我们提出挖掉了。

1,变量写错*1
2,变量写错*2
3,共线胜负逻辑判定
4,&& () 优先级
5,格马蹄变量写错*3
6,注意,有些判定我们还是无法用数组实现,尤其是对于溢出状况的处理

【题意】
在10*9的棋盘中
Bob有一只马和一只将
Alice只有一只将
现在轮到who走(who == 0 则表示Alice先走,who == 1则表示Bob先走)

Alice希望尽可能拖
Bob希望尽可能快

让你输出在这种条件下,棋局的结果

【类型】
有向图博弈

【分析】
我们定义f[10][9][3][3][3][3][2]表示棋盘状态(可能存在马被吃掉的特殊情况)
这个状态数是90*81*2的,不到15000。
最后的[2]表示这一步轮到谁走,0是Alice走,1是Bob走。
值为0表示Alice必胜,否则值为Alice的最长败步数

首先我们要处理处Alice的一步必败态——,列入队首
然后我们也可以出Alice的必胜态——,不考虑这些状态
除去Alice必胜态1的所有状态,都是Alice的必败态。
这时就可以用bfs求最长路。

【时间复杂度&&优化】
O(状态数)

*/


你可能感兴趣的:(题库-HDU,模拟,博弈-SG函数)