作者推荐
【深度优先搜索】【树】【有向图】【推荐】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}{相比n−1根木框,多看到一根木框相比n−1根木框,看到相同的木框最后一根木棍是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();
}
};