【动态规划】【组合数学】1866. 恰有 K 根木棍可以看到的排列数目

简介: 【动态规划】【组合数学】1866. 恰有 K 根木棍可以看到的排列数目

作者推荐

【深度优先搜索】【树】【有向图】【推荐】685. 冗余连接 II

本文涉及知识点

动态规划汇总

LeetCode1866. 恰有 K 根木棍可以看到的排列数目

有 n 根长度互不相同的木棍,长度为从 1 到 n 的整数。请你将这些木棍排成一排,并满足从左侧 可以看到 恰好 k 根木棍。从左侧 可以看到 木棍的前提是这个木棍的 左侧 不存在比它 更长的 木棍。

例如,如果木棍排列为 [1,3,2,5,4] ,那么从左侧可以看到的就是长度分别为 1、3 、5 的木棍。

给你 n 和 k ,返回符合题目要求的排列 数目 。由于答案可能很大,请返回对 109 + 7 取余 的结果。

示例 1:

输入:n = 3, k = 2

输出:3

解释:[1,3,2], [2,3,1] 和 [2,1,3] 是仅有的能满足恰好 2 根木棍可以看到的排列。

可以看到的木棍已经用粗体+斜体标识。

示例 2:

输入:n = 5, k = 5

输出:1

解释:[1,2,3,4,5] 是唯一一种能满足全部 5 根木棍可以看到的排列。

可以看到的木棍已经用粗体+斜体标识。

示例 3:

输入:n = 20, k = 11

输出:647427950

解释:总共有 647427950 (mod 109 + 7) 种能满足恰好有 11 根木棍可以看到的排列。

提示:

1 <= n <= 1000

1 <= k <= n

动态规划

原理

n根木棍,枚举最后一个木棍:

{ 相比 n − 1 根木框,多看到一根木框 最后一根木棍是 n 相比 n − 1 根木框,看到相同的木框 最后一根木棍不是 n \begin{cases} 相比n-1根木框,多看到一根木框 & 最后一根木棍是n \\ 相比n-1根木框,看到相同的木框& 最后一根木棍不是n \\ \end{cases}{相比n1根木框,多看到一根木框相比n1根木框,看到相同的木框最后一根木棍是n最后一根木棍不是n

假定最后一个木框是j ,由于它在木棍n之后,所以必定看不到。删除j,不影响结果。删除后,将大于等于j的木棍减少1,就是1到n-1的一个排列。

动态规划的状态表示

pre[j] 表示i-1根木框,能看到j根木棍的可能数。

dp[j] 表示i+1根木框,能看到j根木棍的可能数。

动态规划的转移方程

处理最后一个木棍为n:

dp={0} dp += pre;

处理最后一根木棍不是n:

dp[j] += pre[j]*(i-1)

除了初始状态,其它状态pre[0]都为0。

初始状态

pre = {1}

动态规划的填表顺序

i从0到大。

动态规划的返回值

pre[k]

代码

核心代码

template<int MOD = 1000000007>
class C1097Int
{
public:
  C1097Int(long long llData = 0) :m_iData(llData% MOD)
  {
  }
  C1097Int  operator+(const C1097Int& o)const
  {
    return C1097Int(((long long)m_iData + o.m_iData) % MOD);
  }
  C1097Int& operator+=(const C1097Int& o)
  {
    m_iData = ((long long)m_iData + o.m_iData) % MOD;
    return *this;
  }
  C1097Int& operator-=(const C1097Int& o)
  {
    m_iData = (m_iData + MOD - o.m_iData) % MOD;
    return *this;
  }
  C1097Int  operator-(const C1097Int& o)
  {
    return C1097Int((m_iData + MOD - o.m_iData) % MOD);
  }
  C1097Int  operator*(const C1097Int& o)const
  {
    return((long long)m_iData * o.m_iData) % MOD;
  }
  C1097Int& operator*=(const C1097Int& o)
  {
    m_iData = ((long long)m_iData * o.m_iData) % MOD;
    return *this;
  }
  bool operator<(const C1097Int& o)const
  {
    return m_iData < o.m_iData;
  }
  C1097Int pow(long long n)const
  {
    C1097Int iRet = 1, iCur = *this;
    while (n)
    {
      if (n & 1)
      {
        iRet *= iCur;
      }
      iCur *= iCur;
      n >>= 1;
    }
    return iRet;
  }
  C1097Int PowNegative1()const
  {
    return pow(MOD - 2);
  }
  int ToInt()const
  {
    return m_iData;
  }
private:
  int m_iData = 0;;
};
class Solution {
public:
  int rearrangeSticks(int n, int k) {
    vector<C1097Int<> > pre = { 1 };
    for (int i = 1; i <= n; i++)
    {
      vector<C1097Int<>> dp = { 0 };
      dp.insert(dp.end(), pre.begin(), pre.end());//最后一根木棍是 i
      for (int j = 1; j < pre.size(); j++)
      {//最后一根木棍是[1,i)
        dp[j] += pre[j] * (i - 1);
      }
      pre.swap(dp);
    }
    return pre[k].ToInt();
  }
};

测试用例

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()
{ 
  int n, k;
  {
    Solution sln;
    n =3 ,k=2 ;
    auto res = sln.rearrangeSticks(n, k);
    Assert(res, 3);
  }
  {
    Solution sln;
    n = 5, k = 5;
    auto res = sln.rearrangeSticks(n, k);
    Assert(res, 1);
  }
  {
    Solution sln;
    n = 20, k = 11;
    auto res = sln.rearrangeSticks(n, k);
    Assert(res, 647427950);
  }
}

2023年2月

class C1097Int

{

public:

C1097Int(int iData = 0) :m_iData(iData)

{

}
 C1097Int  operator+(const C1097Int& o)const
 {
   return C1097Int(((long long)m_iData + o.m_iData) % s_iMod);
 }
 C1097Int&  operator+=(const C1097Int& o)
 {
   m_iData = ((long long)m_iData + o.m_iData) % s_iMod;
   return *this;
 }
 C1097Int  operator*(const C1097Int& o)const
 {
   return((long long)m_iData *o.m_iData) % s_iMod;
 }
 C1097Int&  operator*=(const C1097Int& o)
 {
  m_iData =((long long)m_iData *o.m_iData) % s_iMod;
   return *this;
 }
 int ToInt()const
 {
   return m_iData;
 }

private:

int m_iData = 0;;

static const int s_iMod = 1000000007;

};

int operator+(int iData, const C1097Int& int1097)

{

int iRet = int1097.operator+(C1097Int(iData)).ToInt();

return iRet;

}

int& operator+=(int& iData, const C1097Int& int1097)

{

iData = int1097.operator+(C1097Int(iData)).ToInt();

return iData;

}

int operator*(int iData, const C1097Int& int1097)

{

int iRet = int1097.operator*(C1097Int(iData)).ToInt();

return iRet;

}

int& operator*=(int& iData, const C1097Int& int1097)

{

iData = int1097.operator*(C1097Int(iData)).ToInt();

return iData;

}

template

void MinSelf(T* seft, const T& other)

{

*seft = min(*seft, other);

}

template

void MaxSelf(T* seft, const T& other)

{

*seft = max(*seft, other);

}

int GetNotRepeateNum(int len, int iHasSel)

{

if (0 == len)

{

return 1;

}

if ((0 == iHasSel) && (1 == len))

{

return 10;

}

int iRet = 1;

if (iHasSel > 0)

{

for (int tmp = 10 - iHasSel; (tmp >= 2)&& len ; tmp–,len–)

{

iRet *= tmp;

}

}

else

{

iRet *= 9;

len–;

for (int tmp=9; (tmp>=2)&&len; len–,tmp–)

{

iRet *= tmp;

}

}

return iRet;

}

int GCD(int n1, int n2)

{

int t1 = min(n1, n2);

int t2 = max(n1, n2);

if (0 == t1)

{

return t2;

}

return GCD(t2%t1, t1);

}

void CreateMaskVector(vector& v,const int* const p,int n )

{

const int iMaxMaskNum = 1 << n;

v.resize(iMaxMaskNum);

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

{

v[1 << i] = p[i];

}

for (int mask = 1; mask < iMaxMaskNum ; mask++)

{

const int iSubMask = mask&(-mask);

v[mask] = v[iSubMask] + v[mask-iSubMask];

}

}

class Solution {

public:

int rearrangeSticks(int n, int k) {

vector pre(k + 1);

pre[0] = 1;

for (int iN = 1; iN <= n; iN++)

{

vector dp(k + 1);

for (int j = 1; j <= k; j++)

{

dp[j] = pre[j - 1] + pre[j] * (iN - 1);

}

pre.swap(dp);

}

return pre[k].ToInt();

}

};

2023年9月

class Solution {

public:

int rearrangeSticks(int n, int k) {

vector<C1097Int<>> pre = { 1 };

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

{

vector<C1097Int<>> dp(i + 1);

for (int j = 0; j < pre.size(); j++)

{

dp[j + 1] += pre[j];

dp[j] += pre[j] * (i - 1);

}

pre.swap(dp);

}

return pre[k].ToInt();

}

};


相关文章
|
7月前
|
算法 测试技术 C#
【动态规划】【同余前缀和】【多重背包】[推荐]2902. 和带限制的子多重集合的数目
【动态规划】【同余前缀和】【多重背包】[推荐]2902. 和带限制的子多重集合的数目
|
7月前
|
人工智能 移动开发 算法
【动态规划】【C++算法】LeetCoce996正方形数组的数目
【动态规划】【C++算法】LeetCoce996正方形数组的数目
|
7月前
|
人工智能 测试技术 Windows
【深度优先搜索】【树】【状态压缩】2791. 树中可以形成回文的路径数
【深度优先搜索】【树】【状态压缩】2791. 树中可以形成回文的路径数
|
7月前
|
算法 测试技术 C++
【动态规划 区间dp 位运算】3117. 划分数组得到最小的值之和
【动态规划 区间dp 位运算】3117. 划分数组得到最小的值之和
【动态规划 区间dp 位运算】3117. 划分数组得到最小的值之和
|
7月前
【错题集-编程题】删除相邻数字的最大分数(动态规划 - 线性 dp)
【错题集-编程题】删除相邻数字的最大分数(动态规划 - 线性 dp)
|
7月前
【编程题-错题集】连续子数组最大和(动态规划 - 线性 dp)
【编程题-错题集】连续子数组最大和(动态规划 - 线性 dp)
|
7月前
|
人工智能 算法 BI
【深度优先搜索 图论 树】2872. 可以被 K 整除连通块的最大数目
【深度优先搜索 图论 树】2872. 可以被 K 整除连通块的最大数目
|
7月前
|
算法 测试技术 C#
【动态规划】【数论】【区间合并】3041. 修改数组后最大化数组中的连续元素数目
【动态规划】【数论】【区间合并】3041. 修改数组后最大化数组中的连续元素数目
|
7月前
|
人工智能 算法 BI
【树】【因子数】【数论】【深度优先搜索】2440. 创建价值相同的连通块
【树】【因子数】【数论】【深度优先搜索】2440. 创建价值相同的连通块
|
7月前
|
算法 测试技术 C++
【组合数学】【动态规划】【前缀和】1735生成乘积数组的方案数
【组合数学】【动态规划】【前缀和】1735生成乘积数组的方案数