【动态规划】【中位数】【C++算法】1478. 安排邮筒

简介: 【动态规划】【中位数】【C++算法】1478. 安排邮筒

# 作者推荐

【深度优先搜索】【树】【图论】2973. 树中每个节点放置的金币数目

本文涉及知识点

动态规划汇总

LeetCode1478. 安排邮筒

给你一个房屋数组houses 和一个整数 k ,其中 houses[i] 是第 i 栋房子在一条街上的位置,现需要在这条街上安排 k 个邮筒。

请你返回每栋房子与离它最近的邮筒之间的距离的 最小 总和。

答案保证在 32 位有符号整数范围以内。

示例 1:

输入:houses = [1,4,8,10,20], k = 3

输出:5

解释:将邮筒分别安放在位置 3, 9 和 20 处。

每个房子到最近邮筒的距离和为 |3-1| + |4-3| + |9-8| + |10-9| + |20-20| = 5 。

示例 2:

输入:houses = [2,3,5,12,18], k = 2

输出:9

解释:将邮筒分别安放在位置 3 和 14 处。

每个房子到最近邮筒距离和为 |2-3| + |3-3| + |5-3| + |12-14| + |18-14| = 9 。

示例 3:

输入:houses = [7,4,6,1], k = 1

输出:8

示例 4:

输入:houses = [3,6,14,10], k = 4

输出:0

提示:

n == houses.length

1 <= n <= 100

1 <= houses[i] <= 104

1 <= k <= n

数组 houses 中的整数互不相同。

动态规划

原理

houser[i,j]之间安装邮筒,安装到正中间的房子是最优解。

a,假定房子数是奇数,假定共有2n+1个房子。假定左边有i1个房子,右边有i2个房子。如果i1 < i2,则右移可以缩短距离;i1 > i2,则左移可以缩短距离。如果邮筒不在屋子上,则i1永远不会等于i2。i1==i2,则必定在最中间的屋子。i+(j-i)/2。

b,屋子为偶数,在中间的两坐房子之间,才会让i1和i2。其实中间两间房子的任何一间房子都可以。

可以统一为:i+(j-i)/2

确切的说{ 左边及本身的房子数小于右边的房子数,右移动。 右边及本身的房子数小于左边的房子数,左移动。 \begin{cases} 左边及本身的房子数小于右边的房子数,右移动。\\ 右边及本身的房子数小于左边的房子数,左移动。\\ \end{cases}{左边及本身的房子数小于右边的房子数,右移动。右边及本身的房子数小于左边的房子数,左移动。 → \rightarrow 稳定状态下,必须

{ i 1 > = i 2 , i 1 < = i 2 → i 1 = = i 2 邮筒不在房子上 i 1 + 1 > = i 2 , i 2 + 1 > = i 1 → a b s ( i 1 − i 2 ) < = 1 邮筒在房子上 → \begin{cases} i1 >=i2,i1 <=i2 \rightarrow i1==i2 & 邮筒不在房子上 \\ i1+1>=i2,i2+1 >= i1 \rightarrow abs(i1-i2)<=1 & 邮筒在房子上\\ \end{cases} \rightarrow{i1>=i2,i1<=i2i1==i2i1+1>=i2,i2+1>=i1abs(i1i2)<=1邮筒不在房子上邮筒在房子上

如果房子数量是奇数

{ 邮筒不在房子上 i 1 = = i 2 → ( i 1 + i 2 ) 是偶数 → 房子总数是奇数矛盾 邮筒在房子上且 i 1 等于 i 2 正中间的房子 邮筒在房子上且 i 1 和 i 2 相差 1 假定 11 + 1 = i 2 → i 1 + i 2 + 1 是偶数,和总数是奇数矛盾 → \begin{cases} 邮筒不在房子上& i1==i2 \rightarrow (i1+i2)是偶数 \rightarrow 房子总数是奇数矛盾 \\ 邮筒在房子上且i1等于i2 & 正中间的房子 \\ 邮筒在房子上且i1和i2相差1 & 假定11+1=i2 \rightarrow i1+i2+1是偶数,和总数是奇数矛盾 \\ \end{cases} \rightarrow邮筒不在房子上邮筒在房子上且i1等于i2邮筒在房子上且i1i2相差1i1==i2(i1+i2)是偶数房子总数是奇数矛盾正中间的房子假定11+1=i2i1+i2+1是偶数,和总数是奇数矛盾如果房子的数量是奇数则只能安装在最中间。

如果房子数量是偶数

{ 邮筒不在房子上 i 1 = = i 2 → 中间两间房子的空地 邮筒在房子上且 i 1 等于 i 2 i 1 + i 2 + 1 是奇数,与假设矛盾 邮筒在房子上且 i 1 和 i 2 相差 1 中间任意两间房子 → \begin{cases} 邮筒不在房子上& i1==i2 \rightarrow 中间两间房子的空地 \\ 邮筒在房子上且i1等于i2 & i1+i2+1是奇数,与假设矛盾 \\ 邮筒在房子上且i1和i2相差1 & 中间任意两间房子 \\ \end{cases} \rightarrow邮筒不在房子上邮筒在房子上且i1等于i2邮筒在房子上且i1i2相差1i1==i2中间两间房子的空地i1+i2+1是奇数,与假设矛盾中间任意两间房子

如果房间数是偶数,则中间的两间房子及之间的空地都是最优解。

预处理

vDis[i][j] ,记录一个邮筒到house[i,j]的距离之和。houses要先排序。

动态规划的状态表示

dp[i][j] 表示 j个邮筒支持前i栋房最小距离。

动态规划的状态方程

通过前者状态更新后置状态。

k = 1 i + k < = h o u s e s . l e n g t h \Large_{k=1}^{i+k <= houses.length}k=1i+k<=houses.length pre[i+k][j+1] = min( ⋯ \cdots,pre[i][j]+vDis[⋯ \cdots])

动态规划的初始值

dp[0][0]=0 ,其它INT_MAX,表示非法值。

动态规划的填表顺序

i从小到大,j从小到大。

动态规划的返回值

dp.back()[k]

代码

核心代码

class Solution {
public:
  int minDistance(vector<int>& houses, int K) {
    m_c = houses.size();
    sort(houses.begin(), houses.end());
    vector<vector<int>> vDis(m_c, vector<int>(m_c));
    for (int center = 0; center < m_c; center++)
    {
      {
        int iDis = 0;
        for (int i = center, j = center; (i >= 0) && (j < m_c); i--, j++)
        {
          iDis += houses[j] - houses[i];
          vDis[i][j] = iDis;
        }
      }
      {
        int iDis = 0;
        for (int i = center, j = center + 1; (i >= 0) && (j < m_c); i--, j++)
        {
          iDis += houses[j] - houses[i];
          vDis[i][j] = iDis;
        }
      }
    }
    vector<vector<int>> dp(m_c + 1, vector<int>(K + 1, INT_MAX));
    dp[0][0] = 0;
    for (int i = 0; i <= m_c; i++)
    {
      for (int j = 0; j < K; j++)
      {
        if (INT_MAX == dp[i][j])
        {
          continue;
        }
        for (int m = 1; m + i <= m_c; m++)
        {
          dp[m + i][j + 1] = min(dp[m + i][j + 1],dp[i][j]+vDis[i][i+m-1]);
        }
      }
    }
    return dp.back().back();
  }
  int m_c;
};

测试用例

template<class T>
void Assert(const T& t1, const T& t2)
{
  assert(t1 == t2);
}
template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
  if (v1.size() != v2.size())
  {
    assert(false);
    return;
  }
  for (int i = 0; i < v1.size(); i++)
  {
    Assert(v1[i], v2[i]);
  }
}
int main()
{ 
  vector<int> houses;
  int k;
  {
    Solution sln;
    houses = { 7,4,6,1 };
    k = 1;
    auto res = sln.minDistance(houses, k);
    Assert(8, res);
  }
  {
    Solution sln;
    houses = { 1, 4, 8, 10, 20 };
    k = 3;
    auto res = sln.minDistance(houses, k);
    Assert(5, res);
  }
  {
    Solution sln;
    houses = { 2,3,5,12,18 };
    k = 2;
    auto res = sln.minDistance(houses, k);
    Assert(9, res);
  }
  
  {
    Solution sln;
    houses = { 3,6,14,10 };
    k = 4;
    auto res = sln.minDistance(houses, k);
    Assert(0, res);
  }
}

2023年2月版

class Solution {

public:

int minDistance(vector& houses, int k) {

const int iNotMay = 1000 * 1000 * 10;

std::sort(houses.begin(), houses.end());

m_c = houses.size();

vector pre(m_c);

for (int i = 0; i < m_c; i++)

{

pre[i] = Cost(houses, 0, i + 1);

}

for (int iK = 2; iK <= k; iK++)

{

vector dp(m_c, iNotMay);

for (int iHouse = 0; iHouse < houses.size(); iHouse++)

{

for (int pr = 0; pr < iHouse; pr++)

{

if (iNotMay == pre[pr])

{

continue;

}

dp[iHouse] = min(dp[iHouse], pre[pr] + Cost(houses, pr+1, iHouse + 1));

}

}

pre.swap(dp);

}

return pre.back();

}

int Cost(const vector& houses,int left, int r)

{

int iCost = 0;

int iMean = houses[left + (r - left) / 2];

for (int i = left; i < r; i++)

{

iCost += abs(houses[i] - iMean);

}

return iCost;

}

int m_c;

};

2023年7月版

class Solution {

public:

int minDistance(vector& houses, int k) {

m_c = houses.size();

sort(houses.begin(), houses.end());

vector<vector> vCost(m_c, vector(m_c));

for(int i= 0 ;i < m_c; i++ )

for (int j = i+1; j < m_c; j++)

{

int iMidValue = houses[i] + (houses[j] - houses[i]) / 2;

int cost = 0;

int k = i+1;

for (; houses[k] <= iMidValue; k++)

{

cost += houses[k] - houses[i];

}

for (; k < j; k++)

{

cost += houses[j] - houses[k];

}

vCost[i][j] = cost;

}

const int iNotMay = 1000 * 1000 * 10;

vector<vector> dp(m_c + 1, vector(k + 1, iNotMay));

dp[0][0] = 0;

dp[0][1] = 0;

vector vBegin(m_c);

{

int iSum = 0;

for (int i = 1; i < m_c; i++)

{

iSum += (houses[i] - houses[i - 1]) * i;

vBegin[i] = iSum;

}

}

for (int i = 1; i < m_c; i++)

{

for (int prePos = 0; prePos < i; prePos++)

{

for (int preK = 0; preK < k; preK++)

{

if (iNotMay == dp[prePos][preK])

{

continue;

}

if (0 == preK)

{

dp[i][preK + 1] = vBegin[i];

continue;

}

dp[i][preK + 1] = min(dp[i][preK + 1],dp[prePos][preK] + vCost[prePos][i]);

}

}

}

vector vEnd(m_c);

{

int iSum = 0;

for (int i = m_c - 2; i >= 0; i–)

{

iSum += (houses[i + 1] - houses[i]) * (m_c - 1 - i);

vEnd[i] = iSum;

}

}

int iRet = iNotMay;

for (int i = k-1; i < m_c; i++)

{

iRet = min(iRet, dp[i][k] +vEnd[i]);

}

return iRet;

}

int m_c;

};


相关文章
|
3天前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
17 2
|
11天前
|
存储 算法 安全
基于红黑树的局域网上网行为控制C++ 算法解析
在当今网络环境中,局域网上网行为控制对企业和学校至关重要。本文探讨了一种基于红黑树数据结构的高效算法,用于管理用户的上网行为,如IP地址、上网时长、访问网站类别和流量使用情况。通过红黑树的自平衡特性,确保了高效的查找、插入和删除操作。文中提供了C++代码示例,展示了如何实现该算法,并强调其在网络管理中的应用价值。
|
9天前
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
16天前
|
算法 安全 C++
用 C++ 算法控制员工上网的软件,关键逻辑是啥?来深度解读下
在企业信息化管理中,控制员工上网的软件成为保障网络秩序与提升办公效率的关键工具。该软件基于C++语言,融合红黑树、令牌桶和滑动窗口等算法,实现网址精准过滤、流量均衡分配及异常连接监测。通过高效的数据结构与算法设计,确保企业网络资源优化配置与安全防护升级,同时尊重员工权益,助力企业数字化发展。
35 4
|
2月前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
69 2
|
3月前
|
算法
动态规划算法学习三:0-1背包问题
这篇文章是关于0-1背包问题的动态规划算法详解,包括问题描述、解决步骤、最优子结构性质、状态表示和递推方程、算法设计与分析、计算最优值、算法实现以及对算法缺点的思考。
131 2
动态规划算法学习三:0-1背包问题
|
3月前
|
算法
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
这篇文章介绍了动态规划算法中解决最大上升子序列问题(LIS)的方法,包括问题的描述、动态规划的步骤、状态表示、递推方程、计算最优值以及优化方法,如非动态规划的二分法。
87 0
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
|
3月前
|
算法
动态规划算法学习二:最长公共子序列
这篇文章介绍了如何使用动态规划算法解决最长公共子序列(LCS)问题,包括问题描述、最优子结构性质、状态表示、状态递归方程、计算最优值的方法,以及具体的代码实现。
203 0
动态规划算法学习二:最长公共子序列
|
3月前
|
存储 算法 C++
高精度算法(加、减、乘、除,使用c++实现)
高精度算法(加、减、乘、除,使用c++实现)
855 0
高精度算法(加、减、乘、除,使用c++实现)
|
3月前
|
算法 数据处理 C++
c++ STL划分算法;partition()、partition_copy()、stable_partition()、partition_point()详解
这些算法是C++ STL中处理和组织数据的强大工具,能够高效地实现复杂的数据处理逻辑。理解它们的差异和应用场景,将有助于编写更加高效和清晰的C++代码。
56 0