为了简化,将两种牛分成01。
-
状态表示:$f[i]$表示有
i
个数,最后一位是1的方案总数。 -
划分方式:倒数第二个1的位置。
-
状态计算:$f[i] = f[i-k-1]+f[i-k-2]+…+f[0]$。
-
状态划分:求的是所有方案,将所有方案以最后一个1所在位置划分为:第0位(没有1)、第一位、第二位、… ,即$f[0]+f[1]+f[2]+…+f[n]$,也就是
f[]
数组的前缀和,同时发现f[i]
也就等于s[i-k-1]
#include <iostream>
using namespace std;
const int N = 100010 , mod = 5000011 ;
int f[N] , s[N];
int main()
{
int n , k;
cin >> n >> k;
f[0] = s[0] = 1;//当i-k-1<0时,因为在i是1前面全是0的方案是可行的,所以为了加上这个方案,需要把f[0]设置为1
for(int i = 1 ; i <= n ; i++)
{
f[i] = s[max(i - k - 1 , 0)];
s[i] = (s[i - 1] + f[i]) % mod;
}
cout << s[n] << endl;
return 0;
}
这里是如何计算相邻两个1之间0的个数的?
还有一个问题忘打了,就是我们是如何发现f[i]就是等于s[i-k+1]的啊。。有点表示难以理解。
1 000000有k个 1
然后假设最后一个1的下标是i 第一个1的位置是j
所以 i - j + 1 = k + 2
所以j = i - k + 1
所以f[i] = s[i-k+1]
有点不是很明白这里的f[i],s[i],感觉很乱…题主可以详细解释一下吗。f[0]=s[0]=1,f[i]=s[max(i-k-1),0]这两块不是理解。。。太菜了我。
这里纠正一下,应该是等于
s[i - k - 1]。
$s[]$是$f[]$的前缀和,是为了方便计算状态转移而设计的,因为从状态转移方程可以发现要计算f[i]
,只要计算f[]
的前$i-k-1$数的和。至于为什么取
max(max(i-k-1),0)
,原因是当i
不够大的时候,会取到负数。也可以从实际情况出发,当数字不够多的时候,第i位是1,那么符合条件的方案只能是前面全0,即只有一种方案。这里落了一点,光求出$f[]$是无法解题的,因为$f[i]$仅代表最后一个$1$在第$i$的方案数,这里再将答案的状态划分:划分依据是最后一个$1$的位置,那么可以划分为$n+1$种,$f[0]+f[1]+f[2]+…+f[n]$。而$s[]$恰好是$f[]$的前缀和,所以最后返回的答案就是$s[n]$
写的很棒。
3q