本文涉及知識(shí)點(diǎn)
動(dòng)態(tài)規(guī)劃 狀態(tài)機(jī)dp 性能優(yōu)化
LeetCode3098. 求出所有子序列的能量和
給你一個(gè)長(zhǎng)度為 n 的整數(shù)數(shù)組 nums 和一個(gè) 正 整數(shù) k 。
一個(gè)子序列的 能量 定義為子序列中 任意 兩個(gè)元素的差值絕對(duì)值的 最小值 。
請(qǐng)你返回 nums 中長(zhǎng)度 等于 k 的 所有 子序列的 能量和 。
由于答案可能會(huì)很大,將答案對(duì) 109 + 7 取余 后返回。
示例 1:
輸入:nums = [1,2,3,4], k = 3
輸出:4
解釋:
nums 中總共有 4 個(gè)長(zhǎng)度為 3 的子序列:[1,2,3] ,[1,3,4] ,[1,2,4] 和 [2,3,4] 。能量和為 |2 - 3| + |3 - 4| + |2 - 1| + |3 - 4| = 4 。
示例 2:
輸入:nums = [2,2], k = 2
輸出:0
解釋:
nums 中唯一一個(gè)長(zhǎng)度為 2 的子序列是 [2,2] 。能量和為 |2 - 2| = 0 。
示例 3:
輸入:nums = [4,3,-1], k = 2
輸出:10
解釋:
nums 總共有 3 個(gè)長(zhǎng)度為 2 的子序列:[4,3] ,[4,-1] 和 [3,-1] 。能量和為 |4 - 3| + |4 - (-1)| + |3 - (-1)| = 10 。
提示:
2 <= n == nums.length <= 50
-108 <= nums[i] <= 108
2 <= k <= n
動(dòng)態(tài)規(guī)劃(狀態(tài)機(jī)dp)初版
動(dòng)態(tài)規(guī)劃的狀態(tài) 表示
pre 表示已經(jīng)處理完前x個(gè)數(shù)組符合條件的數(shù)量,dp表示已經(jīng)處理完x+1數(shù)組符合條件的數(shù)量。
pre[i][j][end][len] 表示此子序列:
a,長(zhǎng)度為len。
b,以nums[end]結(jié)束。
c,nums[j]-nums[i]的差最小。如果多個(gè)(i,j)符合條件,取最小的。比如:{1,2,3}的(I,j)是{0,1}而不是{1,2}。
空間復(fù)雜度:O(nnnk)
dp類似。
動(dòng)態(tài)規(guī)劃的轉(zhuǎn)移方程
只需要從x 推導(dǎo)x+1,不需要推導(dǎo)x+2,x+3
?
\cdots
? ,如果硬要的話需要用前綴和(后綴和)?!?br>
{
d
p
=
p
r
e
不選擇
n
u
m
s
[
x
]
d
p
[
i
]
[
j
]
[
x
]
[
l
e
n
+
1
]
+
=
.
.
.
e
l
s
e
且
n
u
m
s
[
j
]
?
n
u
m
s
[
i
]
<
=
n
u
m
s
[
x
]
?
n
u
m
s
[
e
n
d
]
d
p
[
e
n
d
]
[
x
]
[
x
]
[
l
e
n
+
1
]
+
=
.
.
.
e
l
s
e
\begin{cases} dp = pre && 不選擇nums[x] \\ dp[i][j][x][len+1] += ... && else 且 nums[j]-nums[i] <= nums[x]-nums[end] \\ dp[end][x][x][len+1] += ... else \\ \end{cases}
?
?
??dp=predp[i][j][x][len+1]+=...dp[end][x][x][len+1]+=...else??不選擇nums[x]else且nums[j]?nums[i]<=nums[x]?nums[end]?
時(shí)間復(fù)雜度:O(nnnkn) 估計(jì)超時(shí)
剪枝:
枚舉的時(shí)候確保 i < j ,且 j <= x。
動(dòng)態(tài)規(guī)劃+前綴和
拆分成若干個(gè)子問(wèn)題,假定序列存在(i,j),且此序列的能力為power = nums[j]-nums[i]。
動(dòng)態(tài)規(guī)劃的狀態(tài)表示
dp[len][end] 表示 子序列的長(zhǎng)度為len,最后一個(gè)元素是end。
空間復(fù)雜度:O(kn)
利用前綴和優(yōu)化 動(dòng)態(tài)規(guī)劃的轉(zhuǎn)移方程
枚舉end,end not
∈
\in
∈(i,j) ,否則此序列的能量就不是nums[j]-nums[i]了。
{
o
l
d
E
n
d
∈
[
0
,
e
n
d
)
且
n
u
m
s
[
e
n
d
]
?
n
u
m
s
[
o
l
d
E
n
d
]
>
p
o
w
e
r
e
n
d
<
=
i
o
e
d
E
n
d
∈
(
e
n
d
,
n
)
且
n
u
m
s
[
e
n
d
]
?
n
u
m
s
[
o
l
d
E
n
d
]
>
=
p
o
w
e
r
e
n
d
>
=
j
\begin{cases} oldEnd \in [0,end)且nums[end] -nums[oldEnd] > power && end <= i \\ oedEnd \in (end,n) 且 nums[end] -nums[oldEnd] >= power && end >=j \\ \end{cases}
{oldEnd∈[0,end)且nums[end]?nums[oldEnd]>poweroedEnd∈(end,n)且nums[end]?nums[oldEnd]>=power??end<=iend>=j?
如果不利用前綴和優(yōu)先,時(shí)間復(fù)雜度:O(knn),利用前綴和優(yōu)化O(kn)。
總時(shí)間復(fù)雜度:O(knkn)。
動(dòng)態(tài)規(guī)劃的初始狀態(tài)
枚舉所有長(zhǎng)度為2
動(dòng)態(tài)規(guī)劃的填表順序
l e n = 3 n _{len=3}^{n} len=3n?
動(dòng)態(tài)規(guī)劃的返回值
len == k 且 end >=j 才是需要統(tǒng)計(jì)的子序列數(shù)量。
代碼
沒(méi)用前綴和優(yōu)化
理論上過(guò)不了,實(shí)際過(guò)了。
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;
}
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 sumOfPowers(vector<int>& nums, const int K) {
m_c = nums.size();
sort(nums.begin(), nums.end());
C1097Int<> biRet = 0;
for (int i = 0; i < m_c; i++) {
for (int j = i + 1; j < m_c; j++) {
auto cur = Do(nums, i, j, K);
biRet += cur;
//std::cout << " i :" << i << " j:" << j << " " << cur.ToInt() << std::endl;
}
}
return biRet.ToInt();
}
C1097Int<> Do(const vector<int>& nums,int i,int j, const int K) {
const int iDiff = nums[j] - nums[i];
vector<vector<C1097Int<>>> dp(K + 1, vector<C1097Int<>>(m_c));
for (int end = 0; end <= i; end++) {
for (int end1 = 0; end1 < end; end1++) {
if (nums[end] - nums[end1] > iDiff) {
dp[2][end] += 1;
}
}
}
dp[2][j] = 1;
for (int len = 3; len <= K; len++) {
for (int end = 0; end <= i; end++) {
for (int end1 = 0; end1 < end; end1++) {
if (nums[end] - nums[end1] > iDiff) {
dp[len][end] += dp[len - 1][end1];
}
}
}
dp[len][j] = dp[len - 1][i];
for (int end = j+1; end < m_c; end++) {
for (int end1 = j; end1 < end; end1++) {
if (nums[end] - nums[end1] >= iDiff) {
dp[len][end] += dp[len - 1][end1];
}
}
}
}
return std::accumulate(dp.back().begin() + j, dp.back().end(), C1097Int<>())*iDiff;
}
int m_c;
};
測(cè)試用例
int main()
{
vector<int> nums;
int k;
{
Solution sln;
nums = { 6,14,4,13 }, k = 3;
auto res = sln.sumOfPowers(nums, k);
Assert(6, res);
}
{
Solution sln;
nums = { 1,2,3,4 }, k = 3;
auto res = sln.sumOfPowers(nums, k);
Assert(4, res);
}
{
Solution sln;
nums = { 4,3,-1 }, k = 2;
auto res = sln.sumOfPowers(nums, k);
Assert(10, res);
}
{
Solution sln;
nums = { 2,2 }, k = 2;
auto res = sln.sumOfPowers(nums, k);
Assert(0, res);
}
{
Solution sln;
nums = { 2,246006,496910,752786,1013762,1279948,1551454,1828436,2110982,2399316,2693558,2993942,3300640,3613766,3933442,4259696,4592656,4932556,5279494,5633522,5994678,6363102,6739028,7122528,7513792,7913044,8320394,8736004,9160062,9592750,10034184,10484602,10944108,11412852,11891048,12378822,12876346,13383746,13901098,14428528,14966126,15514010,16072380,16641300,17220904,17811360,18412850,19025600,19649778,20285440 }, k = 37;
auto res = sln.sumOfPowers(nums, k);
Assert(273504325, res);
}
}
利用前綴和優(yōu)化:用時(shí)減少不到50%
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;
}
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 sumOfPowers(vector<int>& nums, const int K) {
m_c = nums.size();
sort(nums.begin(), nums.end());
C1097Int<> biRet = 0;
for (int i = 0; i < m_c; i++) {
for (int j = i + 1; j < m_c; j++) {
auto cur = Do(nums, i, j, K);
biRet += cur;
//std::cout << " i :" << i << " j:" << j << " " << cur.ToInt() << std::endl;
}
}
return biRet.ToInt();
}
C1097Int<> Do(const vector<int>& nums, int i, int j, const int K) {
const int iDiff = nums[j] - nums[i];
vector<vector<C1097Int<>>> dp(K + 1, vector<C1097Int<>>(m_c));
for (int end = 0; end <= i; end++) {
for (int end1 = 0; end1 < end; end1++) {
if (nums[end] - nums[end1] > iDiff) {
dp[2][end] += 1;
}
}
}
dp[2][j] = 1;
for (int len = 3; len <= K; len++) {
int end1 = 0;
C1097Int<> biRet = 0;
for (int end = 0; end <= i; end++) {
while ((end1 < end) && (nums[end] - nums[end1] > iDiff)) {
biRet += dp[len - 1][end1];
end1++;
}
dp[len ][end] = biRet;
}
dp[len][j] = dp[len - 1][i];
C1097Int<> biRet2 = 0;
for (int end = j + 1,end1=j ; end < m_c; end++) {
while ((end1 < end) && (nums[end] - nums[end1] >= iDiff)) {
biRet2 += dp[len - 1][end1];
end1++;
}
dp[len][end] = biRet2;
}
}
return std::accumulate(dp.back().begin() + j, dp.back().end(), C1097Int<>()) * iDiff;
}
int m_c;
};
擴(kuò)展閱讀
視頻課程
有效學(xué)習(xí):明確的目標(biāo) 及時(shí)的反饋 拉伸區(qū)(難度合適),可以先學(xué)簡(jiǎn)單的課程,請(qǐng)移步CSDN學(xué)院,聽(tīng)白銀講師(也就是鄙人)的講解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成戰(zhàn)斗了,為老板分憂,請(qǐng)學(xué)習(xí)C#入職培訓(xùn)、C++入職培訓(xùn)等課程
https://edu.csdn.net/lecturer/6176
相關(guān)下載
想高屋建瓴的學(xué)習(xí)算法,請(qǐng)下載《喜缺全書算法冊(cè)》doc版
https://download.csdn.net/download/he_zhidan/88348653
我想對(duì)大家說(shuō)的話 |
---|
聞缺陷則喜是一個(gè)美好的愿望,早發(fā)現(xiàn)問(wèn)題,早修改問(wèn)題,給老板節(jié)約錢。 |
子墨子言之:事無(wú)終始,無(wú)務(wù)多業(yè)。也就是我們常說(shuō)的專業(yè)的人做專業(yè)的事。 |
如果程序是一條龍,那算法就是他的是睛 |
測(cè)試環(huán)境
操作系統(tǒng):win7 開(kāi)發(fā)環(huán)境: VS2019 C++17
或者 操作系統(tǒng):win10 開(kāi)發(fā)環(huán)境: VS2022 C++17
如無(wú)特殊說(shuō)明,本算法用**C++**實(shí)現(xiàn)。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-859919.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-859919.html
到了這里,關(guān)于【動(dòng)態(tài)規(guī)劃 狀態(tài)機(jī)dp 性能優(yōu)化】3098. 求出所有子序列的能量和的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!