丢棋子问题、画匠问题和邮局选址问题

本文通过丢棋子问题、画匠问题和邮局选址问题,探讨了动态规划的解法及四边形不等式优化的应用。丢棋子问题是寻找在最坏情况下扔棋子的最小次数;画匠问题求解多个画匠并行完成画作的最短时间;邮局选址问题则涉及如何在有限邮局数量下,使得所有居民点到邮局总距离最短。每个问题都提供了代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写在代码之前

这三题是书中的最后三题,把它们写在一块是因为三个问题的解法中都有对动态规划解法和对动态规划的四边形不等式优化的解法。问题是在与看了一些动态规划的四边形不等式优化讲解之后还是没有特别理解,在此做个小记录,后面有时间再进一步分析。

丢棋子问题

题目

一座大楼有0~N层,地面算第0层,最高一层为第N层,已知棋子从第0层掉落肯定不会摔碎,从第i层掉落可能摔碎,也可能不会摔碎(1<=i<=N)。给定整数N作为楼层数,再给定整数K作为棋子数,返回如果想找到棋子不会摔碎的最高层数,即使在最差的情况下扔的最少次数,一次只能扔一个棋子。
详细分析参考书籍,根据书上JAVA代码写出C++代码

代码

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

/*暴力递归,时间复杂度会达到O(N!)*/
int process1(int nLevel, int k)
{
   
   
	if (nLevel == 0)
		return 0;
	if (k == 1)
		return nLevel;
	int Min = INT_MAX;
	for (int i = 1; i != nLevel + 1; i++)
	{
   
   
		Min = min(Min, max(process1(i - 1, k - 1), process1(nLevel - i, k)));
	}
	return Min + 1;
}

int solution1(int nLevel, int k)
{
   
   
	if (nLevel < 1 || k < 1)
		return 0;
	return process1(nLevel, k);
}

/*动态规划,对于每个位置枚举时间复杂度为O(N),递归过程中,一共有N×K个位置要枚举,O(N*k)
所以总时间复杂度为O(N^2*K)*/
int solution2(int nLevel, int k)
{
   
   
	if (nLevel < 1 || k < 1)
		return 0;
	if (k == 1)
		return nLevel;
	vector<vector<int>> dp(nLevel + 1, vector<int>(k + 1, 0));
	for (int i = 1; i != nLevel + 1; i++)
		dp[i][1] = i;
	for (int i = 1; i != nLevel + 1; i++)
	{
   
   
		for (int j = 2; j != k + 1; j++)
		{
   
   
			int Min = INT_MAX;
			for (int k = 1; k != i + 1; k++)
			{
   
   
				Min = min(Min, max(dp[k - 1][j - 1], dp[i - k][j]));
			}
			dp[i][j] = Min + 1;
		}
	}
	return dp[nLevel][k];
}

/*动态规划,空间压缩*/
int solution3(int nLevel, int k)
{
   
   
	if (nLevel < 1 || k < 1)
		return 0;
	if (k == 1)
		return nLevel;
	vector<int>preArr(nLevel + 1, 0);
	vector<int>curArr(nLevel + 1, 0);
	for (int i = 1; i != nLevel + 1; i++)
	{
   
   
		preArr[i] = 0;
		curArr[i] = i;
	}
	for (int i = 1; i != k; i++)
	{
   
   
		vector<int> tmp = preArr;
		preArr = curArr;
		curArr = tmp;
		for (int j = 1; j != nLevel + 1; j++)
		{
   
   
			int Min = INT_MAX;
			for (int k = 1; k != j + 1; k++)
				Min = min(Min, max(preArr[k - 1], curArr[j - k]));
			curArr[j] = Min + 1;
		}
	}
	return curArr[nLevel];
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值