{ }codelper
条件语句嵌套条件P0330

买卖机器人:一天只做一件事

按日期顺序给出某件物品 n 天的价格 p_1, p_2, …, p_n。
买卖机器人一次只能持有一件这种物品,开始时没有持有。
机器人每天看当天的价格 p,在下面的规则中恰好只按一条去做。
在持有物品的日子里,设买入价为 c,从买入那天到昨天的价格中最高的为 h,做下面三件事之一。
规则 1:如果 p 不小于 c + g,就以 p 卖出。
规则 2:如果不符合规则 1 且 p 不大于 h - w,就以 p 卖出。
这时如果 p 小于 c,就是亏了,所以从第二天起休息 k 天。
规则 3:如果两条都不符合,就不卖,并且当 p 大于 h 时把 h 提高到 p。
在没有持有物品的日子里,做下面两件事之一。
规则 4:如果还有休息日,不管价格是多少都什么也不做,剩下的休息日减少一天。
规则 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。

输入
第一行给出 n、B、g、w、k,用空格分隔。
第二行给出 p_1 到 p_n,用一个空格分隔。
全部是整数。
1 ≤ n ≤ 10,000,1 ≤ B ≤ 100,000,1 ≤ g ≤ 100,000,1 ≤ w ≤ 100,000,0 ≤ k ≤ 100,1 ≤ p_i ≤ 100,000
输出
机器人每次买入或卖出的日子按日期顺序各输出一行。
买入的日子写成 "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 用 如果 … 否则 如果 … 否则。
陷阱一:外层的分支不要去掉 else 写成 if (持有) { … } 和 if (没有持有) { … } 两个。
在第一个 if 里卖出之后,第二个条件就成立了,同一天还会做规则 4 或 5。
样例第 11 天以 32 卖出后马上又以 32 买入,第 4 天卖出的那天被算作休息日,于是第 6 天以 45 买入。
else 的意思不是“上面的条件现在为假时”,而是“没有走上面那个分支时”。
陷阱二:判断是否亏了的内层 if 只在规则 2 里面。
去掉它、每次按规则 2 卖出都休息,样例第 11 天之后就会休息两天,第 12 天买不了。
以正好等于买入价的价格卖出不算亏(是 <,不是 <=)。
陷阱三:休息日从卖出的第二天开始数。
把卖出的那天算进休息日就少一天,样例第 6 天会以 45 买入。
陷阱四:h 只在规则 3 中提高,每次买入时回到买入价。
忘了提高,样例第 4 天就不卖;买入时不重置,上一次交易的最高价 55 留了下来,第 10 天的 42 就会卖出。
陷阱五:边界全都包含等号。
样例第 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");}