首页 > 其他分享 >牡牛和牝牛

牡牛和牝牛

时间:2023-02-01 12:22:05浏览次数:43  
标签:状态 int 样例 牝牛 牡牛 mod

牡牛和牝牛

约翰要带 $N$ 只牛去参加集会里的展示活动,这些牛可以是牡牛,也可以是牝牛。

牛们要站成一排,但是牡牛是好斗的,为了避免牡牛闹出乱子,约翰决定任意两只牡牛之间至少要有 $K$ 只牝牛。

请计算一共有多少种排队的方法,所有牡牛可以看成是相同的,所有牝牛也一样,答案对 $5000011$ 取模。

输入格式

一行,输入两个整数 $N$ 和 $K$。

输出格式

一个整数,表示排队的方法数。

数据范围

$1 \leq N \leq {10}^{5}$,
$0 \leq K < N$

输入样例:

4 2

输出样例:

6

样例解释

$6$ 种方法分别是:牝牝牝牝,牡牝牝牝,牝牡牝牝,牝牝牡牝,牝牝牝牡,牡牝牝牡。

 

解题思路

  为了方便这里用$1$表示牡牛,用$0$表示牝牛。

  最开始想到用动态规划,然后定义状态$f(i, j)$表示长度为$i$且结尾有$j$个连续的$0$的所有合法字符串的数量,状态转移方程就是

$$f(i,j) =
\begin{cases}
\sum\limits_{u = k}^{i-1}{f(i-1, u)}, \ \ &j = 0 \\\\
f(i-1, j - 1), &j > 0
\end{cases}$$

  即使对状态转移的过程进行优化,时间复杂度最好也是$O(n^2)$。

  然后参考了y总的状态定义,这种定义方法是真的没想到。定义状态$f(i)$表示所有长度为$i$的且以$1$结尾的合法字符串数量。根据上一个$1$出现的位置来划分状态,由于两个$1$之间至少要隔$k$个$0$,因此上一个$1$最近要从下标$i - k - 1$开始。状态转移方程就是$f(i) = 1 + \sum\limits_{j = 1}^{i - k - 1}{f(j)}$,其中$+1$是指前面均是$0$而没有$1$的情况。

  可以发现每次状态转移都是关于$f(i)$的一个前缀和,因此可以开个变量来记录$f(i)$的前缀和,这样状态转移就可以降到$O(1)$,因此整个算法的时间复杂度为$O(n)$。

  AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 1e5 + 10, mod = 5000011;
 5 
 6 int f[N];
 7 
 8 int main() {
 9     int n, m;
10     cin >> n >> m;
11     for (int i = 1, s = 0; i <= n; i++) {
12         f[i] = 1;   // 前面全是0的情况
13         if (i - m - 1 > 0) f[i] = (f[i] + s) % mod; // 至少要隔m个0
14         if (i - m > 0) s = (s + f[i - m]) % mod;    // 累加f[i]的前缀和
15     }
16     int ret = 1;
17     for (int i = 1; i <= n; i++) {  // 最后根据最后一个1出现的位置来统计答案
18         ret = (ret + f[i]) % mod;
19     }
20     cout << ret;
21     
22     return 0;
23 }

 

参考资料

  AcWing 1307. 牡牛和牝牛(算法提高课):https://www.acwing.com/video/717/

标签:状态,int,样例,牝牛,牡牛,mod
From: https://www.cnblogs.com/onlyblues/p/17082146.html

相关文章