爱奇艺2018秋季校招算法工程师(第二场)编程题题解


第一题

题意

一堆石子共N颗,两人(niu和yang)轮流取石子,规则是每次取 4i(i) <script id="MathJax-Element-1" type="math/tex">4^i(i为非负整数)</script>颗,直到轮到的人不能取则为输。两个人都是聪明人。给出N,求赢家名字。

思路

明显是博弈题,SG函数解决,先打表找规律。

b[i] 用来实现mex函数的辅助数组
sg[i] i的sg值
dp[i] 4i <script id="MathJax-Element-2" type="math/tex">4^i</script>

for(int x = 1;x < 1000;x++) {
    memset(b, false, sizeof(b));
    for(int i = 0;i < 16;i++) if(x-dp[i] >= 0)
        b[sg[x-dp[i]]] = true;
    for(int i = 0;i < 1000;i++) if(!b[i]) {
        sg[x] = i;
        break;
    }
}

得到明显的规律

isg(i)
00
11
20
31
42
50
61
70
81
92

sg值周期性循环出现。必败态为 i%5==02 <script id="MathJax-Element-3" type="math/tex">i\%5==0或2</script>

代码

#include <iostream>
using namespace std;
int main() {
    int t;
    cin >> t;
    while(t--) {
        int n;
        cin >> n;
        if(n % 5 == 0 || n % 5 == 2)
            cout << "yang" << endl;
        else
            cout << "niu" << endl;
    }
    return 0;
}

第二题

题意

给出长为N的整数序列,连续的子串里最大值和次大值异或和最大是多少?

思路

不会


第三题

题意

有n+m个人,n个人是A属性,m个人是B属性。每次取出n+m个人里的2个人,其中具有B属性的会改为A属性。问所有人都变为A属性需要取的次数的期望。

思路

dp(n,m) <script id="MathJax-Element-4" type="math/tex">dp(n, m)</script>为需要取的次数的期望。则取一次以后可以到达的状态为 (n,m),(n+1,m1),(n+2,m2) <script id="MathJax-Element-5" type="math/tex">(n,m),(n+1,m-1),(n+2,m-2)</script>。所有可能的种数为 S=(n+m)(n+m1)2 <script id="MathJax-Element-6" type="math/tex">S={(n+m)(n+m-1)\over 2}</script>

编号i状态可能的种数 Ci <script id="MathJax-Element-7" type="math/tex">C_i</script>
1(n,m)n(n-1)/2
2(n+1,m-1)nm
3(n+2,m-2)m(m-1)/2

dp(n,m)=C1S(1+dp(n,m))+C2S(1+dp(n+1,m1))+C3S(1+dp(n+2,m2))C1S(1+dp(n,m))+C2S(1+dp(n+1,m1))0m>1m=1m=0
<script id="MathJax-Element-8" type="math/tex; mode=display">dp(n,m)=\begin{cases} {C_1\over S}(1+dp(n,m)) + {C_2\over S}(1+dp(n+1,m-1)) + {C_3\over S}(1+dp(n+2,m-2)) &\text{m>1}\\ {C_1\over S}(1+dp(n,m)) + {C_2\over S}(1+dp(n+1,m-1)) &\text{m=1}\\ 0 &\text{m=0} \end{cases}</script>

因此可以让m=0开始递增解出 dp(n,m) <script id="MathJax-Element-9" type="math/tex">dp(n,m)</script>

代码

#include <bits/stdc++.h>
using namespace std;
double solve(int n, int m, double y, double z) {
    double sum1, sum2;
    sum1 = 1.0 * n * m * (1 + y);
    sum2 = m >= 2? 1.0 * m * (m-1) / 2 * (1+z) : 0;
    return (sum1 + sum2 + n*(n-1)/2.0) / ((n+m-1)*(n+m)/2.0-n*(n-1)/2);
}
int main() {
    double x, y=0, z = 0;
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 1;i <= m;i++) {
        x = solve(n+m-i, i, y, z);
        y = x;
        z = y;
    }
    printf("%.1f\n", x);
    return 0;
}
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页