{ }codelper
条件分岐入れ子の条件P0330

売買ロボット: 1 日に 1 つだけ

ある品物の n 日間の値段 p_1, p_2, …, p_n が日付の順に与えられる。
売買ロボットはこの品物を一度に 1 個だけ持つことができ、初めは持っていない。
ロボットは毎日その日の値段 p を見て、下の規則からちょうど 1 つだけに従う。
品物を持っている日は、買った値段を c、買った日から昨日までの値段のうち最も高い値段を h として、次の 3 つのうち 1 つをする。
規則 1: p が c + g 以上なら p で売る。
規則 2: 規則 1 に当てはまらず p が h - w 以下なら p で売る。
このとき p が c より小さければ損をしたので、次の日から k 日間休む。
規則 3: どちらでもなければ売らず、p が h より大きければ h を p に上げる。
品物を持っていない日は、次の 2 つのうち 1 つをする。
規則 4: 休む日が残っていれば値段がいくらでも何もせず、残りの休む日が 1 日減る。
規則 5: 休む日が残っておらず p が B 以下なら p で買う。
このとき c と h はどちらも p になる。
p が B より大きければ何もしない。
売った日は、値段が B 以下でも買い直さない。
売った日は品物を持って始まった日だからである。
ロボットが売買した記録を日付の順に求め、売った値段から買った値段を引いたものを売るたびにすべて足した利益と、最後の日が過ぎたあとに品物を持っているかどうかを求めよ。
例では B = 50, g = 20, w = 10, k = 2 である。
2 日目に 48 で買い、3 日目に h が 55 になり、4 日目の 44 は 55 - 10 以下なので売り、損なので 5 日目と 6 日目を休む。
7 日目に 50 で買って 8 日目に 70 で売る。
9 日目に 30 で買い、10 日目に h が 42 になり、11 日目の 32 は 42 - 10 以下なので売る。
今度は損ではないので休まないが、売った日なので 11 日目には買わず、12 日目に 31 で買う。
利益は -4 + 20 + 2 = 18 である。

入力
1 行目に n, B, g, w, k が空白で区切られて与えられる。
2 行目に p_1 から p_n までが空白 1 つで区切られて与えられる。
すべて整数である。
1 ≤ n ≤ 10,000、1 ≤ B ≤ 100,000、1 ≤ g ≤ 100,000、1 ≤ w ≤ 100,000、0 ≤ k ≤ 100、1 ≤ p_i ≤ 100,000
出力
ロボットが買った日または売った日ごとに、日付の順に 1 行ずつ出力する。
買った日は "buy 日付 値段"、売った日は "sell 日付 値段" と書き、日付は 1 から数える。
最後の行に利益と、最後の日が過ぎたあとに品物を持っていれば 1、そうでなければ 0 を空白で区切って出力する。
数はコンマなしで数字だけを書く
例 1 · stdin
12 50 20 10 2
60 48 55 44 40 45 50 70 30 42 32 31
stdout
buy 2 48
sell 4 44
buy 7 50
sell 8 70
buy 9 30
sell 11 32
buy 12 31
18 1
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 8 言語の解答

規則はまず「持っている日 / 持っていない日」に分かれ、その中でもう一度分かれるので、外側の if … else の中に if … else if … else を入れた形そのままに書きます。
どの言語でも骨組みは同じで、言葉だけが違います。
Python は elif とインデントで、JS・TS・C・C++・Java・C# は else if と波かっこで、Craykon は もし … でなければ もし … でなければ で書きます。
罠 1: 外側の分かれ目を else なしで if (持っている) { … } と if (持っていない) { … } の 2 つに分けて書いてはいけません。
最初の if で売ったあとは 2 つ目の条件が真になり、同じ日に規則 4 や 5 までしてしまいます。
例の 11 日目に 32 で売ってすぐ 32 で買い直し、4 日目は売った日が休む日に数えられて 6 日目に 45 で買います。
else は「上の条件が今は偽のとき」ではなく「上の枝に進まなかったとき」です。
罠 2: 損かどうかを見る内側の if は規則 2 の中にだけあります。
これを抜いて規則 2 で売るたびに休むと、例の 11 日目のあとに 2 日休んで 12 日目に買えません。
買った値段とちょうど同じ値段で売ったのは損ではありません(<= ではなく < です)。
罠 3: 休む日は売った次の日から数えます。
売った日を休む日に入れると 1 日足りず、例の 6 日目に 45 で買います。
罠 4: h は規則 3 でだけ上がり、買うたびに買った値段に戻ります。
上げるのを忘れると例の 4 日目に売らず、買うときに戻さないと前の取引の高値 55 が残って 10 日目の 42 で売ります。
罠 5: 境界はすべて「以上・以下」です。
例の 7 日目の 50(= B)、8 日目の 70(= c + g)、11 日目の 32(= h - w)はどれも境界ちょうどです。
利益は最大でも 5 億ほどなので 32 ビット整数に収まります。

Pythonmain.py · 39 行
import sys def main():    data = sys.stdin.read().split()    n, B, g, w, k = (int(v) for v in data[:5])    prices = [int(v) for v in data[5:5 + n]]     holding = False    cost = 0      # 산 값    high = 0      # 산 날부터 지금까지 가장 높았던 값    rest = 0      # 남은 쉬는 날 수    profit = 0    out = []    for day, p in enumerate(prices, start=1):        if holding:            if p >= cost + g:                 # 규칙 1: 이익 실현                out.append(f"sell {day} {p}")                profit += p - cost                holding = False            elif p <= high - w:               # 규칙 2: 고점에서 w 이상 내려옴                out.append(f"sell {day} {p}")                profit += p - cost                holding = False                if p < cost:                  # 손해를 보고 팔았을 때만 쉰다                    rest = k            else:                             # 규칙 3: 그대로 들고 간다                high = max(high, p)        else:                                 # 판 날에는 이쪽으로 오지 않는다            if rest > 0:                      # 규칙 4: 쉬는 날                rest -= 1            elif p <= B:                      # 규칙 5: 산다                out.append(f"buy {day} {p}")                holding = True                cost = p                high = p    out.append(f"{profit} {1 if holding else 0}")    print("\n".join(out)) main()
JavaScriptmain.js · 42 行
const data = require("fs").readFileSync(0, "utf8").split(/\s+/).filter(Boolean).map(Number);const [n, B, g, w, k] = data; let holding = false;let cost = 0; // 산 값let high = 0; // 산 날부터 지금까지 가장 높았던 값let rest = 0; // 남은 쉬는 날 수let profit = 0;const out = [];for (let day = 1; day <= n; day++) {  const p = data[4 + day];  if (holding) {    if (p >= cost + g) {      // 규칙 1: 이익 실현      out.push(`sell ${day} ${p}`);      profit += p - cost;      holding = false;    } else if (p <= high - w) {      // 규칙 2: 고점에서 w 이상 내려옴      out.push(`sell ${day} ${p}`);      profit += p - cost;      holding = false;      if (p < cost) rest = k; // 손해를 보고 팔았을 때만 쉰다    } else {      // 규칙 3: 그대로 들고 간다      high = Math.max(high, p);    }  } else {    // 판 날에는 이쪽으로 오지 않는다    if (rest > 0) {      rest--; // 규칙 4: 쉬는 날    } else if (p <= B) {      // 규칙 5: 산다      out.push(`buy ${day} ${p}`);      holding = true;      cost = p;      high = p;    }  }}out.push(`${profit} ${holding ? 1 : 0}`);console.log(out.join("\n"));
TypeScriptmain.ts · 43 行
import { readFileSync } from "node:fs";const data: number[] = readFileSync(0, "utf8").split(/\s+/).filter(Boolean).map(Number);const [n, B, g, w, k] = data; let holding = false;let cost = 0; // 산 값let high = 0; // 산 날부터 지금까지 가장 높았던 값let rest = 0; // 남은 쉬는 날 수let profit = 0;const out: string[] = [];for (let day = 1; day <= n; day++) {  const p = data[4 + day];  if (holding) {    if (p >= cost + g) {      // 규칙 1: 이익 실현      out.push(`sell ${day} ${p}`);      profit += p - cost;      holding = false;    } else if (p <= high - w) {      // 규칙 2: 고점에서 w 이상 내려옴      out.push(`sell ${day} ${p}`);      profit += p - cost;      holding = false;      if (p < cost) rest = k; // 손해를 보고 팔았을 때만 쉰다    } else {      // 규칙 3: 그대로 들고 간다      high = Math.max(high, p);    }  } else {    // 판 날에는 이쪽으로 오지 않는다    if (rest > 0) {      rest--; // 규칙 4: 쉬는 날    } else if (p <= B) {      // 규칙 5: 산다      out.push(`buy ${day} ${p}`);      holding = true;      cost = p;      high = p;    }  }}out.push(`${profit} ${holding ? 1 : 0}`);console.log(out.join("\n"));
JavaMain.java · 51 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.io.StreamTokenizer; public class Main {    public static void main(String[] args) throws Exception {        StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));        int[] head = new int[5];        for (int i = 0; i < 5; i++) {            in.nextToken();            head[i] = (int) in.nval;        }        int n = head[0], B = head[1], g = head[2], w = head[3], k = head[4];         boolean holding = false;        int cost = 0;    // 산 값        int high = 0;    // 산 날부터 지금까지 가장 높았던 값        int rest = 0;    // 남은 쉬는 날 수        int profit = 0;        StringBuilder out = new StringBuilder();        for (int day = 1; day <= n; day++) {            in.nextToken();            int p = (int) in.nval;            if (holding) {                if (p >= cost + g) {                 // 규칙 1: 이익 실현                    out.append("sell ").append(day).append(' ').append(p).append('\n');                    profit += p - cost;                    holding = false;                } else if (p <= high - w) {          // 규칙 2: 고점에서 w 이상 내려옴                    out.append("sell ").append(day).append(' ').append(p).append('\n');                    profit += p - cost;                    holding = false;                    if (p < cost) rest = k;          // 손해를 보고 팔았을 때만 쉰다                } else {                             // 규칙 3: 그대로 들고 간다                    high = Math.max(high, p);                }            } else {                                 // 판 날에는 이쪽으로 오지 않는다                if (rest > 0) {                      // 규칙 4: 쉬는 날                    rest--;                } else if (p <= B) {                 // 규칙 5: 산다                    out.append("buy ").append(day).append(' ').append(p).append('\n');                    holding = true;                    cost = p;                    high = p;                }            }        }        out.append(profit).append(' ').append(holding ? 1 : 0).append('\n');        System.out.print(out);    }}
C#Main.cs · 58 行
using System;using System.Text; class Program{    static void Main()    {        string[] data = Console.In.ReadToEnd().Split((char[])null, StringSplitOptions.RemoveEmptyEntries);        int n = int.Parse(data[0]), B = int.Parse(data[1]), g = int.Parse(data[2]), w = int.Parse(data[3]), k = int.Parse(data[4]);         bool holding = false;        int cost = 0;    // 산 값        int high = 0;    // 산 날부터 지금까지 가장 높았던 값        int rest = 0;    // 남은 쉬는 날 수        int profit = 0;        var sb = new StringBuilder();        for (int day = 1; day <= n; day++)        {            int p = int.Parse(data[4 + day]);            if (holding)            {                if (p >= cost + g)                   // 규칙 1: 이익 실현                {                    sb.Append("sell ").Append(day).Append(' ').Append(p).Append('\n');                    profit += p - cost;                    holding = false;                }                else if (p <= high - w)              // 규칙 2: 고점에서 w 이상 내려옴                {                    sb.Append("sell ").Append(day).Append(' ').Append(p).Append('\n');                    profit += p - cost;                    holding = false;                    if (p < cost) rest = k;          // 손해를 보고 팔았을 때만 쉰다                }                else                                 // 규칙 3: 그대로 들고 간다                {                    high = Math.Max(high, p);                }            }            else                                     // 판 날에는 이쪽으로 오지 않는다            {                if (rest > 0)                        // 규칙 4: 쉬는 날                {                    rest--;                }                else if (p <= B)                     // 규칙 5: 산다                {                    sb.Append("buy ").Append(day).Append(' ').Append(p).Append('\n');                    holding = true;                    cost = p;                    high = p;                }            }        }        sb.Append(profit).Append(' ').Append(holding ? 1 : 0).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 43 行
#include <algorithm>#include <iostream> int main() {    std::ios::sync_with_stdio(false);    std::cin.tie(nullptr);    int n, B, g, w, k;    std::cin >> n >> B >> g >> w >> k;     bool holding = false;    int cost = 0;    // 산 값    int high = 0;    // 산 날부터 지금까지 가장 높았던 값    int rest = 0;    // 남은 쉬는 날 수    int profit = 0;    for (int day = 1; day <= n; day++) {        int p;        std::cin >> p;        if (holding) {            if (p >= cost + g) {                 // 규칙 1: 이익 실현                std::cout << "sell " << day << ' ' << p << '\n';                profit += p - cost;                holding = false;            } else if (p <= high - w) {          // 규칙 2: 고점에서 w 이상 내려옴                std::cout << "sell " << day << ' ' << p << '\n';                profit += p - cost;                holding = false;                if (p < cost) rest = k;          // 손해를 보고 팔았을 때만 쉰다            } else {                             // 규칙 3: 그대로 들고 간다                high = std::max(high, p);            }        } else {                                 // 판 날에는 이쪽으로 오지 않는다            if (rest > 0) {                      // 규칙 4: 쉬는 날                rest--;            } else if (p <= B) {                 // 규칙 5: 산다                std::cout << "buy " << day << ' ' << p << '\n';                holding = true;                cost = p;                high = p;            }        }    }    std::cout << profit << ' ' << (holding ? 1 : 0) << '\n';}
Cmain.c · 41 行
#include <stdio.h> int main(void) {    int n, B, g, w, k;    scanf("%d %d %d %d %d", &n, &B, &g, &w, &k);     int holding = 0;    int cost = 0;    /* 산 값 */    int high = 0;    /* 산 날부터 지금까지 가장 높았던 값 */    int rest = 0;    /* 남은 쉬는 날 수 */    int profit = 0;    for (int day = 1; day <= n; day++) {        int p;        scanf("%d", &p);        if (holding) {            if (p >= cost + g) {                 /* 규칙 1: 이익 실현 */                printf("sell %d %d\n", day, p);                profit += p - cost;                holding = 0;            } else if (p <= high - w) {          /* 규칙 2: 고점에서 w 이상 내려옴 */                printf("sell %d %d\n", day, p);                profit += p - cost;                holding = 0;                if (p < cost) rest = k;          /* 손해를 보고 팔았을 때만 쉰다 */            } else {                             /* 규칙 3: 그대로 들고 간다 */                if (p > high) high = p;            }        } else {                                 /* 판 날에는 이쪽으로 오지 않는다 */            if (rest > 0) {                      /* 규칙 4: 쉬는 날 */                rest--;            } else if (p <= B) {                 /* 규칙 5: 산다 */                printf("buy %d %d\n", day, p);                holding = 1;                cost = p;                high = p;            }        }    }    printf("%d %d\n", profit, holding);    return 0;}
Craykonmain.cray · 62 行
첫줄 = コンソール.数読み();n = 첫줄[0];B = 첫줄[1];g = 첫줄[2];w = 첫줄[3];k = 첫줄[4];prices = コンソール.数読み(); holding = 偽;cost = 0; // 산 값high = 0; // 산 날부터 지금까지 가장 높았던 값rest = 0; // 남은 쉬는 날 수profit = 0;繰り返す (day = 1; day <= n; day++){    p = prices[day - 1];    もし (holding)    {        もし (p >= cost + g)        {            コンソール.書く("sell " + day + " " + p);            profit = profit + p - cost;            holding = 偽;        }        でなければ もし (p <= high - w)        {            コンソール.書く("sell " + day + " " + p);            profit = profit + p - cost;            holding = 偽;            もし (p < cost)            {                rest = k;            }        }        でなければ        {            high = 数学.大きい方(high, p);        }    }    でなければ    {        もし (rest > 0)        {            rest = rest - 1;        }        でなければ もし (p <= B)        {            コンソール.書く("buy " + day + " " + p);            holding = 真;            cost = p;            high = p;        }    }}もし (holding){    コンソール.書く(profit + " 1");}でなければ{    コンソール.書く(profit + " 0");}