HDU5754 多校联合3 人生赢家Bo

来源:互联网 发布:cs1.5弹道优化参数 编辑:程序博客网 时间:2024/05/21 22:24

题目连接:HDU5754

Life Winner Bo

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


Problem Description
Bo is a "Life Winner".He likes playing chessboard games with his girlfriend G.

The size of the chessboard is N×M.The top left corner is numbered(1,1) and the lower right corner is numberd (N,M).

For each game,Bo and G take turns moving a chesspiece(Bo first).At first,the chesspiece is located at (1,1).And the winner is the person who first moves the chesspiece to (N,M).At one point,if the chess can't be moved and it isn't located at (N,M),they end in a draw.

In general,the chesspiece can only be moved right or down.Formally,suppose it is located at (x,y),it can be moved to the next point (x,y) only if xx and yy.Also it can't be moved to the outside of chessboard.

Besides,There are four kinds of chess(They have movement rules respectively).

1.king.

2.rook(castle).

3.knight.

4.queen.

(The movement rule is as same as the chess.)

For each type of chess,you should find out that who will win the game if they both play in an optimal strategy.

Print the winner's name("B" or "G") or "D" if nobody wins the game.
 

Input
In the first line,there is a number T as a case number.

In the next T lines,there are three numbers type,N and M.

"type" means the kind of the chess.

T1000,2N,M1000,1type4
 

Output
For each question,print the answer.
 

Sample Input
41 5 52 5 53 5 54 5 5
 

Sample Output
GGDB
 

题意:一块棋盘,从(1,1)走到(n,m),有四种棋子,国王,车,马,王后,B先走,问谁会赢。

题目分析:各种典型博弈论的杂粹。棋盘坐标按点算还是按格子算这个无所谓,看个人喜好了。没打过国际象棋还特意科普了一下规则。这里规定了只能严格的往右下走。国王只能走一格,横竖斜着都可以,如果只有横竖的话很好考虑,这个我们放到马一起写。车是横竖可走任意格,抽象一下就是典型的2堆的nim博弈(貌似?),反正取法就是两堆相等时后手必胜。第3个是马,马走的是日型与中国象棋一样。最后一个是王后,王后可以横竖斜着走任意格,同上抽象一下就是威佐夫博弈,直接套模板即可。

下面是马和国王的取法,这2个我是打表写的,首先国王,从终点开始,记为s2[0,0]=-1,B输记为-1,反之记为1,可知s2[1,0]=s[1,1]=s2[0,1]=1,然后每次角标和+1,每层遍历统计所有角标和为i的点下一步会到哪(3个点),如果这3个点有一个是必输的,那它这么走就肯定是必胜了。这3个点有一个是-1就出发点记为1,反之为-1,一层层统计完整个数组。到时候问起来直接查表就好了。

接下来是马。如果可以输可以僵局的话僵局是最优选择。官方题解上说画几个图找规律啥的,我在比赛时图快画烂了也没找到啥。最后还是打表做的。首先剪枝掉角标和不能被3整除的,这些显然会僵局,之后就是同国王了。s[0,0]=s[3,3]=-1,s[2,1]=s[1,2]=1,s[2,4]=s[4,2]=0,这里-1和1的意义同上,0代表僵局,之后的遍历过程也与上文相同。只是每个点有2种走法,看这2部走法有可以必胜的(对于上一个点是必输)就一定是必胜的点了,反之如果有可以僵局的点也可以避免必输,如果全是必输的那就只能是必输的点了。打表做法看似复杂度很高,但是棋盘只有1000*1000,总的计算量1000*1000*(2+3)=5百万很快的30ms,而且查询都是O(1)的操作。

PS:开始规则读错了规则,WA了一片,之后把打表范围取小了,直接WA到怀疑人生,最后看到AC的我终于可以把桌子砸掉了,,

#include <iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>using namespace std;int s[1010][1010];int s2[1010][1010];void init(){    s[2][1]=s[1][2]=1;    s[0][0]=s[3][3]=-1;    s[4][2]=s[2][4]=0;    for(int i=9;i<=2000;i+=3)    {        for(int j=2;2*j<=i;j++)        {            if(i-j>1000||j>1000) continue;            int s1=-s[j-2][i-j-1];            int s2=-s[j-1][i-j-2];            s[j][i-j]=s[i-j][j]=max(s1,s2);        }    }    memset(s2,-1,sizeof(s2));    s2[1][1]=s2[0][1]=s2[1][0]=1;    for(int i=3;i<=2000;i++)    {               for(int j=1;2*j<=i;j++)        {             if(i-j>1000||j>1000) continue;            int a1=-s2[j-1][i-j];            int a2=-s2[j][i-j-1];            int a3=-s2[j-1][i-j-1];            if(a1>0||a2>0||a3>0)                s2[j][i-j]=s2[i-j][j]=1;            else s2[j][i-j]=s2[i-j][j]=-1;        }    }}int main(){    int T,t,m,n;    init();    cin>>T;    while(T--)    {        cin>>t>>n>>m;        int flag;        if(t==1)        {            int buf=s2[n-1][m-1];            if(buf==-1)flag=0;            else flag=1;        }        else if(t==2)        {            if(m==n)                flag=0;            else flag=1;        }        else if(t==3)        {            if((n+m-2)%3!=0) flag=2;            else            {                int k=s[n-1][m-1];                if(k==-1) flag=0;                if(k==0) flag=2;                if(k==1) flag=1;            }        }        else if(t==4)        {            n--;m--;            int k = abs(n-m);            n = n < m? n : m;            int a_k= floor(k*(1.0 + sqrt(5.0))/2);            if(n!=a_k) flag=1;            else flag=0;        }        if(flag==0)cout<<"G"<<endl;        if(flag==1)cout<<"B"<<endl;        if(flag==2)cout<<"D"<<endl;    }}


0 0
原创粉丝点击