[dp算法]逃离农场

【逃离农场】牛牛在农场饲养了n只奶牛,依次编号为0到n-1, 牛牛的好朋友羊羊帮牛牛照看着农场.有一天羊羊看到农场中逃走了k只奶牛,但是他只会告诉牛牛逃走的k 只奶牛的编号之和能被n整除。你现在需要帮牛牛计算有多少种不同的逃走的奶牛群。因为结果可能很大,输出结果对1,000,000,007取模。

例如n = 7 k = 4: 7只

奶牛依次编号为0到6, 逃走了4只

编号和为7的有:{0, 1, 2, 4}

编号和为14的有:{0, 3, 5, 6}, {1, 2, 5, 6}, {1, 3, 4, 6},{2, 3, 4, 5}

4只牛的编号和不会大于18,所以输出5.

输入描述: 输入包括一行,两个整数n和k(1 ≤ n ≤ 1000),(1 ≤ k ≤ 50),以空格分割。

输出描述: 输出一个整数表示题设所求的种数。

解析:

使用dp[i][j][t]表示前i+1头奶牛中选取j头的和除以n余为t的方案数。

则方案分为两种:选取了第i头奶牛和没有选取第i头奶牛两个子问题

状态控制方程为:dp[i][j][t]=dp[i−1][j][t]+dp[i−1][j−1][((t+n)−i)%n]

不用空间压缩的算法:

int func(int n, int k) {
	vector<vector<vector<int>>> dp;
	vector<int> vec1(n,0);   //余数的范围0~n-1共n个,全部初始化为0
	vector<vector<int>> vec2;
	vec2.resize(k+1,vec1); // 逃跑o~k只
	dp.resize(n,vec2);  // n只牛


	//初始赋值 ,i = 0时, 存在1头牛,选出0个,余n为0,这是一种唯一方案; 
	//选出1个,余n为0,也是一种唯一方案
	dp[0][1][0] =dp[0][0][0] = 1;
	//编号为i的前i+1头奶牛
	for (auto i = 1; i < n; i++) 
	{
		//选择j头奶牛。注意j的退出条件是min(k,i+1),因为i对应有i+1头牛
		for (auto j = 0; j <= min(k, i+1); j++)
		{
			for (auto t = 0; t < n; ++t)//余t
			{
				//因为这里会涉及到 j-1,需要确保它大于0
				if (j > 0)
				{ 
					dp[i][j][t] = (dp[i-1][j][t] + dp[i-1][j - 1][((t + n) - i) % n]);
				} 
				else
				{
					//当j==0的时候,也就是一头牛都没逃走,这时余数必须是0才有值,取其他都是0
					if (j == 0 && t==0)
					{ 
						dp[i][j][t] = 1;
					}
				}
			}
		}
	}
	return dp[n-1][k][0];
}

 注意初始化条件和j的退出条件

留意到状态转移方程,其实每次计算新的i的时候,它只会用到i-1的二维数组的值。所以可以只使用一个二维数组去保存状态值就行了。所以状态方程可以写成

dp[j][t] = (dp[j][t] + dp[j - 1][((t + n) - i) % n]);
int func2(int n, int k) {
    vector<vector<int>> dp;
    vector<int> vec(n,0);
    dp.resize(k + 1, vec);
    
    dp[1][0] = dp[0][0] = 1; //网上有些写法,只写了dp[0][0] = 1其实是不对的
    for (auto i = 1; i < n; i++)//前i+1头奶牛
    {
        for (auto j = min(k, i+1); j > 0; --j)//选择j头奶牛。
        {
            for (auto t = 0; t < n; ++t)//余t
            {
                dp[j][t] = (dp[j][t] + dp[j - 1][((t + n) - i) % n]) ;
            }
        }
    }
    return dp[k][0];
}

注意:j要使用递减的方式,因为状态方程依赖于j-1,如果我们使用递增,就会先计算了j-1,然后再计算j,这样,j-1就变成了当前i循环的j-1,而不是上一次i循环的j-1。比如先计算i=3,当计算i=4时,先计算j-1,计算完了j-1之后,然后你再计算j的时候,i=3的j-1已经被覆盖,你使用的就是i=4的j-1,这是不对的。所以一定要用递减的方式循环j

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值