{ }codelper
记录列表记录删除元素P0247

装满就出发的快递卡车

快递箱按到达顺序装上卡车。
一辆卡车可以一直装到总重量达到 C 为止。
每个箱子有运单号、目的地和重量。
箱子到达时,按以下规则选择卡车。
- 在还没出发的卡车中,找目的地与这个箱子相同、装上这个箱子后总重量仍不超过 C 的卡车。
- 这样的卡车有多辆时,装上编号最小的那辆。
不是剩余空间最大或最小的那辆。
- 没有这样的卡车时,叫一辆新卡车来装。
卡车按被叫来的顺序编号 1, 2, 3, …,新卡车的目的地就是这个箱子的目的地。
- 装上箱子后,如果那辆卡车的总重量恰好等于 C,卡车立即出发。
出发的卡车不再装货。
所有箱子装完后,还没出发的卡车按编号顺序出发。
第一行给出 C 和箱子数 n,接下来 n 行按到达顺序给出"运单号 目的地 重量",用一个空格隔开。
第一行输出卡车数。
接着按出发顺序,每辆卡车输出一行:"编号 目的地 总重量 箱子数",后面是这辆车按装车顺序装的运单号,全部用一个空格隔开。
试着把一个箱子和一辆卡车分别打包成记录(结构体),并处理卡车记录的列表。

输入
1 ≤ n ≤ 1,000,1 ≤ C ≤ 1,000,000,1 ≤ 重量 ≤ C(整数)。
运单号由 1~8 个英文大写字母和数字组成,互不相同。
目的地是 1~10 个英文小写字母。
输出
一行卡车数,接着每辆卡车一行
样例 1 · stdin
10 7
P1 seoul 6
P2 busan 4
P3 seoul 5
P4 busan 6
P5 seoul 4
P6 seoul 3
P7 seoul 5
stdout
4
2 busan 10 2 P2 P4
1 seoul 10 2 P1 P5
3 seoul 8 2 P3 P6
4 seoul 5 1 P7
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

打包一辆卡车(编号、目的地、总重量、运单列表)的方法:Python @dataclass(列表字段要用 field(default_factory=list)),Java 用字段可改的 class(record 的字段不能改),C# class,C、C++ struct,JS、TS 对象 { no, dest, load, codes: [] }(TS 用 interface Truck 写出形状)。
Craykon 没有类,所以每辆卡车做一个 字典()(t["load"]、t["codes"]),用 waiting.添加(t) 放进列表。
关键在于修改选中的卡车。
Python、JS、TS、Java、C# 的 class、Craykon 的字典里,从列表取出的值指向同一辆卡车,所以 truck.load += w 会改到列表里的卡车。
C 要像 Truck *t = &trucks[k] 这样用指针,C++ 要像 Truck &t = trucks[k] 这样用引用来拿;C++ 的 vector 变大时会搬动元素,手里的引用会失效,所以要先 reserve,或者按下标重新找。
出发的卡车要从等待列表中移除:Python remove,JS、TS splice,Java、C# Remove,Craykon 按位置移除。
五个陷阱:① 写成 load + w < C 就选不到恰好装到 C 的卡车,会去叫新车。
② Python 里像 def __init__(self, no, dest, codes=[]) 这样把默认值设成 [],所有卡车会共用这一个列表,运单号就混在一起(@dataclass 直接拒绝这种默认值)。
③ C# 里把卡车写成 struct,Truck truck = waiting[at]; 是副本,改了 Load 列表里也不变(C 的 Truck t = trucks[k]; 也一样)。
④ 按卡车编号顺序输出是错的:装满先出发的卡车在前。
⑤ 只看目的地相同的最后一辆卡车(next-fit),会漏掉前面卡车剩下的空间。

Pythonmain.py · 38 行
import sysfrom dataclasses import dataclass, field  @dataclassclass Truck:    no: int    dest: str    load: int = 0    codes: list[str] = field(default_factory=list)  # 트럭마다 새 목록  def main():    data = sys.stdin.read().split()    cap, n = int(data[0]), int(data[1])    trucks = 0    waiting: list[Truck] = []   # 아직 떠나지 않은 트럭(번호 순)    departed: list[Truck] = []  # 떠난 차례    for i in range(n):        code, dest, w = data[2 + 3 * i], data[3 + 3 * i], int(data[4 + 3 * i])        truck = next((t for t in waiting if t.dest == dest and t.load + w <= cap), None)        if truck is None:            trucks += 1            truck = Truck(trucks, dest)            waiting.append(truck)        truck.load += w        truck.codes.append(code)        if truck.load == cap:  # 꽉 차면 곧바로 떠난다            waiting.remove(truck)            departed.append(truck)    departed.extend(waiting)  # 남은 트럭은 번호 순으로    out = [str(len(departed))]    for t in departed:        out.append(f"{t.no} {t.dest} {t.load} {len(t.codes)} " + " ".join(t.codes))    print("\n".join(out))  main()
JavaScriptmain.js · 27 行
const data = require("fs").readFileSync(0, "utf8").trim().split(/\s+/);const cap = Number(data[0]);const n = Number(data[1]);let count = 0;const waiting = []; // 아직 떠나지 않은 트럭(번호 순)const departed = []; // 떠난 차례for (let i = 0; i < n; i++) {  const code = data[2 + 3 * i];  const dest = data[3 + 3 * i];  const w = Number(data[4 + 3 * i]);  let truck = waiting.find((t) => t.dest === dest && t.load + w <= cap);  if (truck === undefined) {    count++;    truck = { no: count, dest, load: 0, codes: [] }; // 트럭마다 새 객체·새 배열    waiting.push(truck);  }  truck.load += w;  truck.codes.push(code);  if (truck.load === cap) {    waiting.splice(waiting.indexOf(truck), 1);    departed.push(truck);  }}departed.push(...waiting);const out = [String(departed.length)];for (const t of departed) out.push(`${t.no} ${t.dest} ${t.load} ${t.codes.length} ${t.codes.join(" ")}`);console.log(out.join("\n"));
TypeScriptmain.ts · 34 行
interface Truck {  no: number;  dest: string;  load: number;  codes: string[];} const data: string[] = require("fs").readFileSync(0, "utf8").trim().split(/\s+/);const cap = Number(data[0]);const n = Number(data[1]);let count = 0;const waiting: Truck[] = []; // 아직 떠나지 않은 트럭(번호 순)const departed: Truck[] = []; // 떠난 차례for (let i = 0; i < n; i++) {  const code = data[2 + 3 * i];  const dest = data[3 + 3 * i];  const w = Number(data[4 + 3 * i]);  let truck: Truck | undefined = waiting.find((t) => t.dest === dest && t.load + w <= cap);  if (truck === undefined) {    count++;    truck = { no: count, dest, load: 0, codes: [] };    waiting.push(truck);  }  truck.load += w;  truck.codes.push(code);  if (truck.load === cap) {    waiting.splice(waiting.indexOf(truck), 1);    departed.push(truck);  }}departed.push(...waiting);const out: string[] = [String(departed.length)];for (const t of departed) out.push(`${t.no} ${t.dest} ${t.load} ${t.codes.length} ${t.codes.join(" ")}`);console.log(out.join("\n"));
JavaMain.java · 59 行
import java.io.*;import java.util.*; public class Main {    // 트럭 하나: 실은 상자가 늘어나니 record가 아니라 필드를 바꿀 수 있는 클래스    static class Truck {        final int no;        final String dest;        int load = 0;        final List<String> codes = new ArrayList<>();         Truck(int no, String dest) {            this.no = no;            this.dest = dest;        }    }     public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(br.readLine());        int cap = Integer.parseInt(st.nextToken());        int n = Integer.parseInt(st.nextToken());        int count = 0;        List<Truck> waiting = new ArrayList<>();        List<Truck> departed = new ArrayList<>();        for (int i = 0; i < n; i++) {            st = new StringTokenizer(br.readLine());            String code = st.nextToken();            String dest = st.nextToken();            int w = Integer.parseInt(st.nextToken());            Truck truck = null;            for (Truck t : waiting) {                if (t.dest.equals(dest) && t.load + w <= cap) {                    truck = t;                    break;                }            }            if (truck == null) {                truck = new Truck(++count, dest);                waiting.add(truck);            }            truck.load += w;            truck.codes.add(code);            if (truck.load == cap) {                waiting.remove(truck);                departed.add(truck);            }        }        departed.addAll(waiting);        StringBuilder sb = new StringBuilder();        sb.append(departed.size()).append('\n');        for (Truck t : departed) {            sb.append(t.no).append(' ').append(t.dest).append(' ').append(t.load).append(' ').append(t.codes.size());            for (String c : t.codes) sb.append(' ').append(c);            sb.append('\n');        }        System.out.print(sb);    }}
C#Main.cs · 56 行
using System;using System.Collections.Generic;using System.Text; // class라서 목록에서 꺼낸 t는 같은 트럭을 가리킨다(struct였다면 복사본)class Truck{    public int No;    public string Dest;    public int Load;    public List<string> Codes = new List<string>();     public Truck(int no, string dest)    {        No = no;        Dest = dest;    }} class Program{    static void Main()    {        var data = Console.In.ReadToEnd().Split((char[])null, StringSplitOptions.RemoveEmptyEntries);        int cap = int.Parse(data[0]);        int n = int.Parse(data[1]);        int count = 0;        var waiting = new List<Truck>();        var departed = new List<Truck>();        for (int i = 0; i < n; i++)        {            string code = data[2 + 3 * i];            string dest = data[3 + 3 * i];            int w = int.Parse(data[4 + 3 * i]);            Truck truck = waiting.Find(t => t.Dest == dest && t.Load + w <= cap);            if (truck == null)            {                truck = new Truck(++count, dest);                waiting.Add(truck);            }            truck.Load += w;            truck.Codes.Add(code);            if (truck.Load == cap)            {                waiting.Remove(truck);                departed.Add(truck);            }        }        departed.AddRange(waiting);        var sb = new StringBuilder();        sb.Append(departed.Count).Append('\n');        foreach (var t in departed)            sb.Append($"{t.No} {t.Dest} {t.Load} {t.Codes.Count} {string.Join(" ", t.Codes)}\n");        Console.Write(sb);    }}
C++main.cpp · 57 行
#include <iostream>#include <string>#include <vector> struct Truck {    int no;    std::string dest;    int load = 0;    std::vector<std::string> codes;}; int main() {    std::ios::sync_with_stdio(false);    std::cin.tie(nullptr);    int cap, n;    std::cin >> cap >> n;    std::vector<Truck> trucks;    trucks.reserve(n);           // 상자마다 많아야 한 대: 자리를 미리 잡아 옮겨지지 않게    std::vector<int> waiting;    // 아직 떠나지 않은 트럭의 자리(번호 순)    std::vector<int> departed;   // 떠난 차례    for (int i = 0; i < n; i++) {        std::string code, dest;        int w;        std::cin >> code >> dest >> w;        int pick = -1, at = -1;        for (int k = 0; k < (int)waiting.size(); k++) {            const Truck &t = trucks[waiting[k]];            if (t.dest == dest && t.load + w <= cap) {                pick = waiting[k];                at = k;                break;            }        }        if (pick < 0) {            trucks.push_back(Truck{(int)trucks.size() + 1, dest});            pick = (int)trucks.size() - 1;            waiting.push_back(pick);            at = (int)waiting.size() - 1;        }        Truck &t = trucks[pick]; // 참조라서 목록 안의 트럭이 바뀐다        t.load += w;        t.codes.push_back(code);        if (t.load == cap) {            waiting.erase(waiting.begin() + at);            departed.push_back(pick);        }    }    departed.insert(departed.end(), waiting.begin(), waiting.end());    std::cout << departed.size() << '\n';    for (int k : departed) {        const Truck &t = trucks[k];        std::cout << t.no << ' ' << t.dest << ' ' << t.load << ' ' << t.codes.size();        for (const auto &c : t.codes) std::cout << ' ' << c;        std::cout << '\n';    }    return 0;}
Cmain.c · 69 行
#include <stdio.h>#include <string.h> #define MAXN 1000 typedef struct {    char code[9];    char dest[11];    int w;    int next; /* 같은 트럭에 다음으로 실린 상자(없으면 -1) */} Parcel; typedef struct {    int no;    char dest[11];    int load;    int count;    int first, last; /* 실은 상자들의 첫·마지막 번호 */    int gone;        /* 떠났으면 1 */} Truck; static Parcel parcels[MAXN];static Truck trucks[MAXN]; /* 상자마다 많아야 한 대 새로 생긴다 */static int order[MAXN];    /* 떠난 차례 */ int main(void) {    int cap, n, nt = 0, nd = 0;    scanf("%d %d", &cap, &n);    for (int i = 0; i < n; i++) {        Parcel *p = &parcels[i];        scanf("%8s %10s %d", p->code, p->dest, &p->w);        p->next = -1;        Truck *t = NULL; /* 고른 트럭을 포인터로 쥐어야 목록 안의 트럭이 바뀐다 */        for (int k = 0; k < nt; k++) {            if (!trucks[k].gone && strcmp(trucks[k].dest, p->dest) == 0 && trucks[k].load + p->w <= cap) {                t = &trucks[k];                break;            }        }        if (t == NULL) {            t = &trucks[nt];            t->no = ++nt;            strcpy(t->dest, p->dest);            t->load = 0;            t->count = 0;            t->first = -1;            t->gone = 0;        }        t->load += p->w;        t->count++;        if (t->first < 0) t->first = i;        else parcels[t->last].next = i;        t->last = i;        if (t->load == cap) {            t->gone = 1;            order[nd++] = t->no - 1;        }    }    for (int k = 0; k < nt; k++)        if (!trucks[k].gone) order[nd++] = k;    printf("%d\n", nd);    for (int j = 0; j < nd; j++) {        const Truck *t = &trucks[order[j]];        printf("%d %s %d %d", t->no, t->dest, t->load, t->count);        for (int i = t->first; i >= 0; i = parcels[i].next) printf(" %s", parcels[i].code);        printf("\n");    }    return 0;}
Craykonmain.cray · 51 行
// 크레이콘에는 클래스가 없어서 트럭 한 대를 사전 하나로 묶는다(no, dest, load, codes)첫줄 = 控制台.读数字();cap = 첫줄[0];n = 첫줄[1];count = 0;waiting = []; // 아직 떠나지 않은 트럭(번호 순)departed = []; // 떠난 차례循环 (i = 0; i < n; i++){    p = 控制台.读行().去空白().分割(" ");    code = p[0];    dest = p[1];    w = p[2].转数字();    at = -1;    循环 (k = 0; k < waiting.数量; k++)    {        如果 (waiting[k]["dest"] == dest && waiting[k]["load"] + w <= cap)        {            at = k;            跳出;        }    }    如果 (at < 0)    {        count++;        t = 字典(); // 트럭마다 새 사전, 새 목록        t["no"] = count;        t["dest"] = dest;        t["load"] = 0;        t["codes"] = [];        waiting.添加(t);        at = waiting.数量 - 1;    }    truck = waiting[at]; // 같은 사전을 가리킨다: 고치면 목록 안의 트럭이 바뀐다    truck["load"] = truck["load"] + w;    truck["codes"].添加(code);    如果 (truck["load"] == cap)    {        waiting.按位置移除(at);        departed.添加(truck);    }}遍历 (t 在 waiting){    departed.添加(t);}控制台.输出(departed.数量);遍历 (t 在 departed){    控制台.输出(t["no"] + " " + t["dest"] + " " + t["load"] + " " + t["codes"].数量 + " " + t["codes"].连接(" "));}