问题描述
小蓝是工厂里的安全工程师,他负责安放工厂里的危险品。
工厂是一条直线,直线上有 n 个空位,小蓝需要将若干个油桶放置在 n 个空位上,每 2 个油桶中间至少需要 k 个空位隔开,现在小蓝想知道有多少种放置油桶的方案,你可以编写一个程序帮助他吗?
由于这个结果很大,你的输出结果需要对 取模。
输入格式
第一行包含两个正整数n,k,分别表示 n 个空位与 k 个隔开的空位。
输出格式
输出共 1行,包含 1 个整数,表示放置的方案数对 取模。
样例输入
4 2
样例输出
6
说明
用 0代表不放,1 代表放,6 种情况分别为:
0000
,1000
,0100
,0010
,0001
,1001
。
评测数据规模
对于所有评测数据,
思路:
当放一个油桶时,每一个空位都有一个可能
当放两个油桶时,则前 k
个空位不能放油桶,最多在第 i-k-1 个位置才能放油桶,
方案数等于 dp[i-k-1]
。
解题代码:
#include <iostream>
using namespace std;
typedef long long ll;
const ll N=1e6+10,p=1e9+7;
ll dp[N],prefix[N];
int main()
{
int n,k;cin>>n>>k;
dp[0]=prefix[0]=1;
for(int i=1;i<=n;i++)
{
if(i-k-1<1)dp[i]=1;
else dp[i]=prefix[i-k-1];
prefix[i]=(prefix[i-1]+dp[i])%p;
}
cout<<prefix[n]<<'\n';
return 0;
}
1、动态规划定义
-
dp[i]
:表示前i
个空位的放置方案数。 -
prefix[i]
:表示前i
个空位的前缀和,即prefix[i] = dp[0] + dp[1] + ... + dp[i]
。
2、动态规划转移方程
-
对于第
i
个空位:-
如果选择不放油桶,则方案数等于
dp[i-1]
。 -
如果选择放油桶,则前
k
个空位不能放油桶,方案数等于dp[i-k-1]
。
-
-
因此,动态规划转移方程为:
dp[i] = dp[i-1] + dp[i-k-1]
其中:
-
dp[i-1]
:不放油桶的方案数。 -
dp[i-k-1]
:放油桶的方案数。
-
3、前缀和的作用
-
前缀和
prefix[i]
用于快速计算dp[i]
。 -
由于
dp[i]
依赖于dp[i-1]
和dp[i-k-1]
,而dp[i-1]
可以通过prefix[i-1] - prefix[i-2]
计算,dp[i-k-1]
可以直接通过prefix[i-k-1]
计算。 -
使用前缀和可以将动态规划的转移优化为
O(1)
的时间复杂度。
4、示例分析
假设输入:
n = 4, k = 2
(1) 初始化
-
dp[0] = prefix[0] = 1
。
(2) 动态规划转移
-
i = 1
:-
i - k - 1 = 1 - 2 - 1 = -2 < 1
,所以dp[1] = 1
。 -
prefix[1] = (prefix[0] + dp[1]) % p = (1 + 1) % p = 2
。
-
-
i = 2
:-
i - k - 1 = 2 - 2 - 1 = -1 < 1
,所以dp[2] = 1
。 -
prefix[2] = (prefix[1] + dp[2]) % p = (2 + 1) % p = 3
。
-
-
i = 3
:-
i - k - 1 = 3 - 2 - 1 = 0 < 1
,所以dp[3] = 1
。 -
prefix[3] = (prefix[2] + dp[3]) % p = (3 + 1) % p = 4
。
-
-
i = 4
:-
i - k - 1 = 4 - 2 - 1 = 1 >= 1
,所以dp[4] = prefix[1] = 2
。 -
prefix[4] = (prefix[3] + dp[4]) % p = (4 + 2) % p = 6
。
-
(3) 结果
-
prefix[4] = 6
,表示有 6 种放置方案。
5、为什么返回 prefix[n]
?
-
prefix[n]
表示前n
个空位的总方案数。 -
因为
dp[i]
只表示前i
个空位的方案数,而prefix[i]
是dp[0]
到dp[i]
的累加和。 -
最终答案需要包括所有可能的方案数,因此返回
prefix[n]
。
6、动态规划的过程总结
-
定义状态:
dp[i]
表示前i
个空位的放置方案数。 -
转移方程:
dp[i] = dp[i-1] + dp[i-k-1]
。 -
前缀和优化:使用
prefix[i]
快速计算dp[i]
。 -
初始化:
dp[0] = prefix[0] = 1
。 -
结果:返回
prefix[n]
,表示前n
个空位的总方案数。