0%

洛谷P2252 取石子游戏 题解

同学在做这道题,我就一起做了,没想到比他A得还要早。这是一个考试中比较实用的做法。

题目链接

对于\(mn \le 5000^2\)

首先针对nm比较小的情况,由于

1
"必胜情况存在,当且仅当存在策略,使对手不存在必胜情况"

然后我们就得到了dp公式递推公式
时间复杂度\(O(mn)\) ## 对于更大的情况 ### 70分做法 我们发现绝大多数情况都是可以必胜的,直接输出1
当然出题人不会这么轻易放过你的,所以只能拿70分 ### 100分做法 我们把打的表格放出来,发现存在一个规律,即必败的点在两条直线上,而且经过原点,这就是规律。
至于为什么在两条直线上,是因为nm是可以互换的。
然后我们求出斜率的平均值,最后判断一下就好了。
判断的时候直接相等肯定是不行的,因为这是浮点数比较,还是我们估计的浮点数,所以我们需要一个eps。这个eps,可以用统计的方法算出来。 #### 求斜率和eps 首先剔除n>m情况的数据
\(k\)为我们的估计的斜率,有\[k=\sum \frac{y}{x}\] 然后有 \[eps = \frac {\sum |k'-k|} n\] 其中\(k'\)是实际的斜率
\(|\frac nm - slope| \le eps\)的情况下,极大概率是必败的。当然也要考虑mn颠倒的情况。 ## 源程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <bits/stdc++.h>
using namespace std;
const int N = 5010;

int n, m, vis_r[N], vis_c[N], vis_x[N << 1]; // x - y + N exist 0

int G[N][N];

double slope = 1.618959789221876;
double eps = 3e-4;
int main() {
scanf("%d%d", &n, &m);
if(n < N && m < N)
{
for(int i = 0; i <= n; i++) {
for(int j = 0; j <= m; j++) {
// val[i][j]
if(!vis_r[i] && !vis_c[j] && !vis_x[i - j + N]) {
G[i][j] = 0;
vis_r[i] = vis_c[j] = vis_x[i - j + N] = 1;
}
else {
G[i][j] = 1;
}
}
}
cout << G[n][m] << endl;

}
else
{
if(abs(1.0*n/m - slope) <= eps || abs(1.0*m/n - slope) <= eps)
cout << 0 << endl;
else
cout << 1 << endl;
}

return 0;
}

附打表和绘图程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/python3
import matplotlib.pyplot as plt
import numpy
xx = []
yy = []
with open("data.txt", "r") as f:
for line in f:
s = line.split()
x, y = int(s[0]), int(s[1])
xx.append(x)
yy.append(y)
maxslope = 0
minslope = 1e10
slopey = []
slope = 0
for x, y in zip(xx, yy):
maxslope = max(y/x, maxslope)
minslope = min(y/x, minslope)
slope += y / x
slopey.append(y/x)


slope /= len(xx)

# plt.scatter(xx, yy)

plt.scatter(xx, slopey)

x = numpy.linspace(0, 5000, 5000)
# plt.plot(x, maxslope * x)
# plt.plot(x, minslope * x)
print(minslope, slope)

plt.show()