版权声明:本文为博主原创文章,可以转载但是必须声明版权。 https://blog.csdn.net/forever_shi/article/details/83189155
题意:
桌上有N 堆糖果,第i 堆糖果有Ai 个糖。两人在玩游戏,轮流进行,每次进行下列两个操作中的一个
1.将当前最大的那堆糖果全部吃完
2.将每堆糖果吃掉一个
吃完的人输,假设两人足够聪明,问谁能必胜 1<=n<=1e5
输出First(表示第一个人必胜),或Second(表示第二个人必胜)
题解:
这题感觉没有图不好讲啊,真是道很神的题啊。先放个官方题解的链接。下面的图片来自官方题解。
官方题解
我们可以把a数组先从大到小排序,排序后可以看作下图,其中高度表示
的数值。
我们之后可以把删除一堆和每一堆去掉一个看作下图:
然后我们可以把一开始的图看作一个方格图,把删除一堆看作向右走,把每一堆减少一个看作向上走一步,轮廓线对应原图中的结束状态。如下图所示:
对于这种博弈思想的题,经常是判断每个状态是先手必胜还是先手必败,这里也是用这种思路,我们用’x’表示先手必败,用’o’表示先手必胜。首先显然对于终结节点是先手必胜,因为已经所有的糖都被吃掉了。然后我们可以暴力计算其他所有点的状态,但是这题显然会T。我们考虑观察这个状态图(如下),我们会发现,在同一条对角线上的点的状态都是一样的,要么都是必胜,要么都是必败。那么我们只需要一直沿着对角线走,直到走到边缘。我们发现走到边缘之后,如果走到当前行的最右端或者列的最右端有至少一种能用奇数步完成,那么就是必胜的,否则就是必败的。于是就可以快速计算这题的答案了。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,a[100010];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
sort(a+1,a+n+1);
reverse(a+1,a+n+1);
for(int i=1;i<=n;++i)
{
if(i+1>a[i+1])
{
int ji=i+1,pd=0;
while(a[ji]==i)
{
pd^=1;
++ji;
}
if(pd||(a[i]-i)%2==1)
printf("First\n");
else
printf("Second\n");
break;
}
}
return 0;
}