似乎该博弈了!(动态规划)

题目来源:https://biancheng.love/contest-ng/index.html#/41/problems

F 似乎该博弈了!

题目描述

nova君陷入了困境,因为他无法在PS4游戏上凭借操作战胜对手。机智如他,只好和对手博弈了!

nova君拿出的方案和以往有些不一样,他说:“你可以决定石子数量和石子的取法,我来决定先后手,这样非常公平。”他的对手觉得nova君说的有道理,于是不仅决定了每局的石子数量,还给出了k个数字,表示每次可以任意取走数量等同于这k个数字中一个的石子,k中一定有一个为1。先取完者为胜。

现在很急很关键,快帮nova君看看他到底应该先手还是后手才能战胜对手。

输入

每组测试数据两行。

第一行两个整数n和k,第二行k个整数,意义如题目描述。

N<=100000,k<=15

输出

对于每组数据,输出一行,为nova应该采取的先后手 sente gote

输入样例

4 3
1 2 3
1 1
1 

输出样例

gote
sente

解题思路:给定n个石头,和k种去除石头的方式,每种方式可以去除一定量的石头, 现在Sente(简称S),gote(简称O),S先手,O后手,每次每个人能选择一种去除石头的方式,谁去除最后一堆谁就赢了。要求出必胜之人是谁。
分析:
1.用一个dp数组记录,对于先手者能取到的记录为1,后手者为0.
2.初始dp数组都为0,遍历1到n,如果dp[i]为0,说明上一手是后手取得,这样先手就能取,把dp[i]变为1,由于是从1到n,这样每个状态记录时,前面的都已经记录好了,所以是可行的.
3.这样最后只需要判断dp[n]是1,还是0,就可以判断是先手胜还是后手胜了。
状态转移方程为:if (i - mjmj[j] >= 0 && !dp[i - mjmj[j]])  dp[i] = 1.
给出代码:
 1 #include <bits/stdc++.h>
 2 #define MAX 1000010
 3 int dp[MAX],mjmj[15];
 4 
 5 int n,m,i,j;
 6 int main()
 7 {
 8     while(~scanf("%d",&n))
 9     {
10         memset(dp,0,sizeof(dp));
11         scanf("%d",&m);
12         for (i=0; i<m; i++)
13         {
14             scanf("%d",&mjmj[i]);
15         }
16         for (i=1;i<=n;++i)
17         {
18             for (j=0;j<m;++j)
19             {
20                 if (i-mjmj[j]>=0&&!dp[i-mjmj[j]])
21                 {
22                     dp[i]=1;
23                     break;
24                 }
25             }
26         }
27         if (dp[n])
28             printf("sente
");
29         else
30             printf("gote
");
31     }
32     return 0;
33 }

推荐博客http://blog.csdn.net/kjc19920531/article/details/8120989

推荐博客http://blog.csdn.net/wangtaoking1/article/details/7308117

 
原文地址:https://www.cnblogs.com/zpfbuaa/p/5075680.html