スタートアップのインタビュー中にこの質問をされましたが、最近のコンテストでこれを見ました
**質問 :
一連の日の株価が与えられます。毎日、1単位の在庫を購入するか、すでに購入した任意の数の在庫単位を販売するか、何もしません。取引戦略を最適に計画することで獲得できる最大利益はいくらですか?**
例(入力、つまり日数は異なる場合があります)
5 3 2 => profit = 0 //価格は毎日低下するため、最大利益= 0
1 2 100 =>利益= 197
1 3 1 2 => profit = 3 // 1で購入3で販売、その後1で購入、2で販売..総利益= 3
私のソリューション:
a)株価が最も高かった日を見つけます。その日まで在庫を1単位購入してください。
b)その日が最終日である場合、終了します:
その他:その日のすべての株を売り、その日の後に配列を分割し、残りの要素で再帰します
c)利益を統合する
例1 4 1 2 3
a)2日目の最高株価.. 1日目に在庫を購入し、2日目に売却し(利益= 3)、残りの日に再帰します:1 2 3
b)最高価格は3(5日目)なので、3日目と4日目に在庫を購入し、5日目に売ります(利益=(3 * 2-3 = 3)
c)総利益= 3 + 3 = 6
このための複雑さはO(n ^ 2)であることがわかりました。このソリューションは11のケースのうち10をパスしましたが、最後のテストケースの時間制限を超えました(つまり、最大の入力)
だから私の質問は誰でもこの問題に対するより効率的な解決策を考えることができますか?動的プログラミングの解決策はありますか?
追伸:ここで質問するのは初めてです。この質問を改善/追加する必要がある場合はお知らせください
メソッドのロジックに同意しますが、再帰処理やグローバルな最大値検索を行う必要はありません。販売/購入日を見つけるには、毎日1回だけ見る必要があります。
トリックは最後から始めることです。 時間をさかのぼって移動すれば株式取引は簡単です!
コードが言葉より読みやすいと思うなら、私の説明をスキップしてください、しかし、ここに行きます:
最後から読んで、その日の価格を見てください。これは(最後から)これまでの最高価格ですか?それから売ります!最後の日(私たちが読み始めた日)は、常に販売します。
その後、次の日に移動します(時間をさかのぼってください)。それはこれまでの最高価格ですか(まだ見たすべてから)? -その後、すべてを売って、あなたはより良い日を見つけることができません。それ以外の場合は、価格が上昇するため、購入します。最初まで同じ方法を続けます。
問題全体は1つの逆ループで解決されます:取引の決定と利益の両方を計算します。
Cに似たpythonのコードは次のとおりです(ほとんどのPythonのようなものは避けた。
def calcprofit(stockvalues):
dobuy=[1]*len(stockvalues) # 1 for buy, 0 for sell
prof=0
m=0
for i in reversed(range(len(stockvalues))):
ai=stockvalues[i] # shorthand name
if m<=ai:
dobuy[i]=0
m=ai
prof+=m-ai
return (prof,dobuy)
例:
calcprofit([1,3,1,2]) gives (3, [1, 0, 1, 0])
calcprofit([1,2,100]) gives (197, [1, 1, 0])
calcprofit([5,3,2]) gives (0, [0, 0, 0])
calcprofit([31,312,3,35,33,3,44,123,126,2,4,1]) gives
(798, [1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0])
m
は、最後から見た最高の株価であることに注意してください。 ai==m
その後、その段階で購入した株式からの利益は0です。その時点以降、価格が低下または安定しており、購入しませんでした。
単純なループで利益計算が正しいことを確認できます(簡単にするために、上記の関数内にあると想像してください)
stock=0
money=0
for i in range(len(stockvalues)):
if dobuy[i]:
stock+=1
money-=stockvalues[i]
else:
money+=stockvalues[i]*stock
stock=0
print("profit was: ",money)
このタスクの別のO(n)解は、maxがminよりも大きいインデックスを持つ必要があることを知って、maxとminの間の最良の差(利益)を見つけるローカル最小と最大を使用して実行できます。以前の最良のローカル最小値を調べる必要があります(C#実装)。
public int[] GetBestShareBuyingStrategy(int[] price)
{
var n = price.Length;
if (n <= 1)
return null;
var profit = 0;
var min = 0;
var max = 0;
var lmin = 0;
for (var i = 1; i < n; i++)
{
var lmax = i;
var lp = price[lmax] - price[lmin];
if (lp <= 0)
{
lmin = i;
}
else
{
var tp = price[lmax] - price[min];
if (lp > tp && lp > profit)
{
min = lmin;
max = lmax;
profit = lp;
}
else if (tp > profit)
{
max = lmax;
profit = tp;
}
}
}
return profit > 0
? new [] {min, max}
: null;
}
[Test]
[TestCase(new[] { 10, 9, 8, 7, 3 })]
[TestCase(new[] { 5, 5, 5, 5, 5 })]
[TestCase(new[] { 5, 4, 4, 4 })]
[TestCase(new[] { 5, 5, 3, 3 })]
public void GetBestShareBuyingStrategy_When_no_sense_to_buy(int[] sharePrices)
{
var resultStrategy = GetBestShareBuyingStrategy(sharePrices);
Assert.IsNull(resultStrategy);
}
[Test]
[TestCase(new[] { 10, 8, 12, 20, 10 }, 1, 3)]
[TestCase(new[] { 5, 8, 12, 20, 30 }, 0, 4)]
[TestCase(new[] { 10, 8, 2, 20, 10 }, 2, 3)]
[TestCase(new[] { 10, 8, 2, 20, 10 }, 2, 3)]
[TestCase(new[] { 10, 2, 8, 1, 15, 20, 10, 22 }, 3, 7)]
[TestCase(new[] { 1, 5, 2, 7, 3, 9, 8, 7 }, 0, 5)]
[TestCase(new[] { 3, 5, 2, 7, 3, 9, 8, 7 }, 2, 5)]
public void GetBestShareBuyingStrategy_PositiveStrategy(int[] sharePrices, int buyOn, int sellOn)
{
var resultStrategy = GetBestShareBuyingStrategy(sharePrices);
Assert.AreEqual(buyOn, resultStrategy[0]);
Assert.AreEqual(sellOn, resultStrategy[1]);
}
。再帰の必要がないように配列の最後から開始
1。 smax =リストからの最大株価
2。次に、smaxまでのすべての株式を購入し、smaxの価格で販売すると仮定して利益を求めます
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int numOfTestCase = sc.nextInt();
for (int i = 0; i < numOfTestCase; i++) {
int n = sc.nextInt();
long profit = 0;
int[] stockPrice = new int[n];
for (int j = 0; j < n; j++) {
stockPrice[j] = sc.nextInt();
}
int currMax = Integer.MIN_VALUE;
for (int j = n - 1; j >= 0; j--) {
if (currMax < stockPrice[j]) {
currMax = stockPrice[j];
}
profit += (currMax - stockPrice[j]);
}
System.out.println(profit);
}
}
コンテストサイトでその問題を解決しました。私は受け入れられた答えよりも簡単なアルゴリズムを得たと思います。
1. smax = maximum stock price from the list
2. then find the profit by assuming you have bought all the stocks till smax
and you sell it at the price of smax
3. then check if smax is the last element of the stock price list
if yes then return profit as answer,
if no
then make a new list containing stock prices after smax to the last stock price
and repeat steps 1-3 and keep adding profit of each iteration to get the final profit.
こちらがよりシンプルで理解しやすいアルゴです。
private static void BuyOnceAndSellONce()
{
int[] stock = new int[] { 100, 180, 260, 310, 40, 535, 695 };
int profit = 0;
int minimumPrice = int.MaxValue;
for (int i = 0; i < stock.Length; i++)
{
profit = Math.Max(profit, stock[i] - minimumPrice);
minimumPrice = Math.Min(stock[i], minimumPrice);
}
Console.WriteLine("profit " + profit);
}
private static void MultipleBuySellButNonOverlappingTransactions()
{
int[] stock = new int[] { 100, 180, 260, 310, 40, 535, 695 };
int totalProfit = 0;
int currentProfit = 0;
for (int i = 1; i < stock.Length;i++)
{
currentProfit = stock[i] - stock[i - 1];
if (currentProfit > 0)
totalProfit += currentProfit;
}
Console.WriteLine(totalProfit);
}
private static int MaxProfit(int[] A)
{
if (A.Length == 0)
return 0;
Stack<int> repositoryStack = new Stack<int>();
int maxProfit = 0;
int tempProfit;
for (int i = 0; i < A.Length; i++)
{
if (repositoryStack.Count == 0)
{
repositoryStack.Push(i);
continue;
}
while (repositoryStack.Count != 0 && A[i] < A[repositoryStack.Peek()])
{
repositoryStack.Pop();
}
if (repositoryStack.Count != 0 && A[i] > A[repositoryStack.Peek()])
{
tempProfit = A[i] - A[repositoryStack.Peek()];
if (tempProfit > maxProfit)
maxProfit = tempProfit;
}
if(repositoryStack.Count == 0)
repositoryStack.Push(i);
}
return maxProfit;
}
あなたの論理は正しい...
グローバルな最大値で販売します。しかし、再帰は必要ありません...
i番目の要素がグローバルな最大値である場合... iの前にすべての銘柄を売る!
問題は以前のanswer + i + 1 to N ...
再帰は必要ありません...線形に計算できます!