152.積最大部分配列
トピックの説明:
整数配列を指定してください nums
。配列内で最大の積を持つ空でない連続部分配列を見つけて (部分配列には少なくとも 1 つの数値が含まれます)、その部分配列を返します。対応商品。
テスト ケースの答えは 32 ビット 整数です。
サブ配列 は、配列の連続したサブシーケンスです。
問題解決のアイデア:
この質問は「部分配列の最大合計」と非常によく似ており、それに倣って状態表現と状態遷移を定義できます。
i.
dp[i]
表⽰以
i
为结尾的所有⼦数组的最⼤乘积,
ii.
dp[i] = max(nums[i], dp[i - 1] * nums[i])
;
正負の符号が存在するため、簡単に求めることができるため、
dp[i]
dp[i -
1]
の情報では、
dp[i]
。たとえば、配列
[-2, 5, -2]
では、上記の状態遷移を使用すると、 が得られます。
によって取得される dp 配列は
[-2, 5, -2]
であり、最大積は
5
。ただし、実際の最大積はすべての数値の乗算である必要があり、結果は になります。
果为
20
。
その理由は、
dp[2]
を求めるとき、
nums [2] であるためです。
は負の数なので、必要なのは
i - 1
位置の最後にある最小の積 (
-10)
)"、このようにして、負の数値に「最小値」を乗算すると実際の値が得られます
最大値。
したがって、「
dp
製品の最大値の表」だけでなく、「最小値の表」も必要になります。製品の値「
dp
テーブル」。
1.
状态表⽰:
f[i]
の意味:
i
、
g[i]
は、
i
で終わるすべての部分配列の最小積を意味します。
2.
状態移行プロセス:
各位置を移動するときは、2 つの
dp
配列の値を同時に更新する必要があります。
for
f[i]
、つまり「
i
次の 3 つの形式があります。
i.
⼦数组的⻓度为
1
,也就是
nums[i]
;
ii.
⼦数组的⻓度⼤于
1
,但
nums[i] > 0
,此时需要的是
i - 1
为结尾的所有⼦数组
的最⼤乘积
f[i - 1]
,再乘上
nums[i]
,也就是
nums[i] * f[i - 1]
;
iii.
⼦数组的⻓度⼤于
1
,但
nums[i] < 0
,此时需要的是
i - 1
为结尾的所有⼦数组
的最⼩乘积
g[i - 1]
,再乘上
nums[i]
,也就是
nums[i] * g[i - 1]
;
(
nums[i] = 0
の場合、すべての部分配列の積は
0
、実際には 3 つの状況がすべて含まれます)
前述のとおり、
f[i] = max(nums[i], max(nums[i] * f[i - 1], nums[i] * g[i -
1]))。
for
g[i]
、つまり「
i
次の 3 つの形式があります。
i.
⼦数组的⻓度为
1
,也就是
nums[i]
;
ii.
⼦数组的⻓度⼤于
1
,但
nums[i] > 0
,此时需要的是
i - 1
为结尾的所有⼦数组
的最⼩乘积
g[i - 1]
,再乘上
nums[i]
,也就是
nums[i] * g[i - 1]
;
iii.
⼦数组的⻓度⼤于
1
,但
nums[i] < 0
,此时需要的是
i - 1
为结尾的所有⼦数组
的最⼤乘积
f[i - 1]
,再乘上
nums[i]
,也就是
nums[i] * f[i - 1]
;
综上所述,
g[i] = min(nums[i], min(nums[i] * f[i - 1], nums[i] * g[i -
1]))
。
(
nums[i] = 0
の場合、すべての部分配列の積は
0
、実際には 3 つの状況がすべて含まれます)
3.
初始化:
初期化を支援するために、先頭に補助ノードを追加できます。この手法を使用する場合、次の 2 つの点に注意してください。
i.
補助ノードの値は、後続のフォーム入力が正しいことを保証する必要があります。
ii.
添字のマッピング関係。
この質問では、先頭にグリッドを追加し、
f[0] = g[0] = 1
つまり Can とします。
4.
填表顺序:
状態転送プロセスによれば、フォームに記入する順序は「左から右へ、2 つのフォームを一緒に記入する」ことになります。
5.
返回值:
返回
f
表中的最⼤值。
解決策コード:
class Solution {
public:
int maxProduct(vector<int>& nums) {
int n=nums.size();
if(n==1)return nums[0];
vector<int>f(n,0);
vector<int>g(n,0);
f[0]=nums[0];g[0]=nums[0];
for(int i=1;i<n;i++)
{
f[i]=max(max(f[i-1]*nums[i],g[i-1]*nums[i]),nums[i]);
g[i]=min(min(g[i-1]*nums[i],nums[i]*f[i-1]),nums[i]);
}
int ret=INT_MIN;
for(int i=0;i<n;i++)ret=max(ret,f[i]);
return ret;
}
};
1567. 積が正の数になる最長の部分配列の長さ
トピックの説明:
整数配列を指定してください nums
。その積が正の数になる最長の部分配列の長さを見つけてください。
配列の部分配列は、元の配列の 0 個以上の連続した数値で構成される配列です。
積が正の数になる最長の部分配列の長さを返してください。
問題解決のアイデア:
アルゴリズムのアイデア:
引き続き「最大サブ配列合計」の状態表現に従って、この問題の解決を試みてください。
ステータスの意味:
dp[i]
は、「i で終わるすべての部分配列の中で最も長い部分配列で、その積が正の数である」ことを意味します。 「配列の長さ」。
思考状態の転送:
nums[i] < で
i
a i= 4> については、次の 3 つの状況で議論できます。
i.
If
nums[i] = 0
、すべての < a i=4>i
で終わる部分配列の積を正の数にすることはできません。この場合
dp[i] = 0
;
ii.
如果
nums[i] > 0
,那么直接找到
dp[i - 1]
的值(这⾥请再读⼀遍
dp[i -
1]
は意味を表し、
dp[i - 1]
の場合のノット値を考慮します。 0 の場合、効果は結果に影響しません)、 を追加します。
⼀即可,此时
dp[i] = dp[i - 1] + 1
;
iii.
もし
nums[i] < 0
なら、痛みがあるはずです。現在の状況では、 それを入手する方法はありませんからです。
の最大長。掛け算では「負のマイナスは正になる」ため、
dp[i - 1]
に依存するだけでは を推定することはできません。
出
dp[i]
的值。
ただし、「
i - 1
で終わるすべての部分配列」がわかっている場合、その積が負の数である最も長い部分配列の長さは < /span>
度」
neg[i - 1]
,那么此时的
dp[i]
是不是就等于
neg[i - 1] + 1
呢?
上記の分析を通じて、最終結果を推定するには 2 つの
dp
テーブルが必要であると結論付けることができます。 「製品」だけが必要なわけではありません
「積が負の数になる最大の部分配列」と「積が負の数になる最大の部分配列」も必要です。
1.
状态表⽰:
f[i]
の意味:
i
で終わるすべての部分配列のうち、積は長さ「正の数」の最も長い部分配列の
g[i]
の意味:
i
で終わるすべての部分配列のうち、積は長さ「負の数」の最長の部分配列。
2.
状態移行プロセス:
各位置を移動するときは、2 つの
dp
配列の値を同時に更新する必要があります。
対象
f[i]
、つまり
i
は、最終積が「正の数」である最大の部分配列です。
nums[i]
の値に従って、
3 つの状況に分けて説明します。
i.
nums[i] = 0
、すべて
i < i=4> で終わる部分配列の積を正の数にすることはできません。この場合、
f[i] =
0
;
ii.
nums[i] > 0
时,那么直接找到
f[i - 1]
的值(这⾥请再读⼀遍
f[i - 1]
代表
を検討し、
f[i - 1]
のノット値が
0 0 であるかどうかを検討します。 a>
、効果は結果に影響しません)、1 つ追加します。
今回
f[i] = f[i - 1] + 1
;
iii.
nums[i] < 0
时,此时我们要看
g[i - 1]
的值(这⾥请再读⼀遍
g[i - 1]
代
テーブルの意味。負の値は正の値になるため、積が
i - 1
で終わる最長の部分配列が負の数 であることがわかっている場合、
長さは、
g[i - 1] < の値に従って、
1
を追加するだけです/span>
は 2 つの状況に分けられます:
1.
g[i - 1] = 0
、
i - 1 で説明します。 a>i - 1
a>
は、積が負の数で終わる最長の部分配列です。 <
なぜなら
nums[i] < 0
、つまり
i
最後に正の積を持つ最大の部分配列も存在しません。これ
时
f[i] = 0
;
2.
g[i - 1] != 0
、
i - 1 < で説明します。 /span>
は、積が負の数で終わる最大の部分配列です。 <
なぜなら
nums[i] < 0
、つまり
i
最後に正の積を持つ最長の部分配列は
g[i - に等しい
1] + 1
;
上で説明したように、
nums[i]
f[i] = g[i - 1] == 0 ? 0 : g[i - 1] +
のとき、
0
1;
for
g[i]
、つまり
i
は、最終積が「負の数」である最大の部分配列です。
nums[i]
の値に従って、
3 つの状況に分けて説明します。
i.
nums[i] = 0
、すべて
i < i=4> で終わる部分配列の積を負の数にすることはできません。この場合、
g[i] =
0
;
ii.
nums[i] < 0
时,那么直接找到
f[i - 1]
的值(这⾥请再读⼀遍
f[i - 1]
代表
を検討し、
f[i - 1]
のノット値が
0 0 であるかどうかを検討します。 a>
、効果は結果に影響しません)、
(正の数 * 負の数 = 負の数であるため)、このとき
g[i] = f[i - 1] + 1
;
iii.
nums[i] > 0
时,此时我们要看
g[i - 1]
的值(这⾥请再读⼀遍
g[i - 1]
代
テーブルの意味。正の数 * 負の数 = 負の数) であるため、
g[i - 1]
の値に応じて、次の 2 つの状況が存在します。
1.
g[i - 1] = 0
、
i - 1 で説明します。 a>i - 1
a>
は、積が負の数で終わる最長の部分配列です。 <
なぜなら
nums[i] > 0
、つまり
i
積が負の数で終わる最大の部分配列も存在しません。これ
时
f[i] = 0
;
2.
g[i - 1] != 0
、
i - 1 < で説明します。 /span>
は、積が負の数で終わる最大の部分配列です。 <
なぜなら
nums[i] > 0
、つまり
i
最後に正の積を持つ最長の部分配列は
g[i - に等しい
1] + 1
;
上で説明したように、
nums[i] > 0
のとき、
g[i] = g[i - 1] == 0 ? 0 : g[i - 1] +
1
;
「正の数と負の数」については常に議論されるため、ここでの導出はかなり複雑ですが、以下のルールに厳密に従うだけで済みます。
この状態で必要な dp
配列を セルで見つけます。
i.
正の数 * 正の数 = 正の数
ii.
負の数 * 負の数 = 正の数
iii.
負の数 * 正の数 = 正の数 * 負の数 = 負の数
3.
初始化:
初期化を支援するために、先頭に「補助ノード」を追加できます。この手法を使用する場合、次の 2 つの点に注意してください。
i.
補助ノードの値は、「後続のフォーム入力が正しいことを保証する」必要があります。
ii.
「添字マッピング関係」。
この質問では、先頭にグリッドを追加し、
f[0] = g[0] = 0
つまり Can とします。
4.
填表顺序:
「状態伝達方程式」によれば、フォームに記入する順序は「左から右へ、2 つのフォームを一緒に記入する」です。
5.
返回值:
「ステータス表現」に従って、
f
テーブルの最大値を返したいと考えています。
解決策コード:
class Solution {
public:
int getMaxLen(vector<int>& nums) {
int n=nums.size();
vector<int>f(n+1,0);
vector<int>g(n+1,0);
int ret=INT_MIN;
for(int i=1;i<=n;i++)
{
if(nums[i-1]>0)
{
f[i]=f[i-1]+1;
g[i]=g[i-1]==0?0:g[i-1]+1;
}
else if(nums[i-1]<0)
{
g[i]=f[i-1]+1;
f[i]=g[i-1]==0?0:g[i-1]+1;
}
ret=max(ret,f[i]);
}
return ret;
}
};