โยนคนที่อ้วนที่สุดออกจากเครื่องบินมากเกินไป


200

สมมติว่าคุณมีเครื่องบินและมีเชื้อเพลิงน้อย เว้นแต่ว่าเครื่องบินจะลดน้ำหนักผู้โดยสารลง 3,000 ปอนด์มันจะไม่สามารถไปถึงสนามบินถัดไปได้ เพื่อช่วยชีวิตคนจำนวนมากที่สุดเราอยากจะโยนคนที่หนักที่สุดออกจากเครื่องบินก่อน

และใช่มีผู้คนนับล้านบนเครื่องบินและเราต้องการอัลกอริธึมที่เหมาะสมที่สุดในการค้นหาผู้โดยสารที่หนักที่สุดโดยไม่จำเป็นต้องเรียงลำดับรายการทั้งหมด

นี่เป็นปัญหาพร็อกซีสำหรับสิ่งที่ฉันพยายามรหัสใน C ++ ฉันต้องการจะทำ "partial_sort" ในรายการผู้โดยสารโดยน้ำหนัก แต่ฉันไม่ทราบว่าฉันต้องการองค์ประกอบกี่ชิ้น ฉันสามารถใช้อัลกอริทึม "partial_sort" ของตัวเอง ("partial_sort_accumulate_until") ได้ แต่ฉันสงสัยว่ามีวิธีที่ง่ายกว่าในการทำสิ่งนี้โดยใช้ STL มาตรฐาน


5
หากการเปรียบเทียบกับมนุษย์ถือคุณสามารถเริ่มต้นด้วยการทิ้งคนที่มีน้ำหนักมากกว่า X เช่น 120 กิโลกรัมเนื่องจากมีแนวโน้มว่าจะเป็นคนที่อ้วนที่สุด
RedX

132
ผู้โดยสารทุกคนจะร่วมมือกับอัลกอริทึมใด ๆ หรือไม่?
Lior Kogan

34
หัวข้อเช่นนี้คือเหตุผลที่ฉันรักมัน
Markus

14
ฉันสามารถขอสายการบินนี้ได้เพื่ออะไร ฉันต้องการให้แน่ใจว่าฉันจะบินไปกับพวกเขาก่อนเทศกาลวันหยุดเท่านั้นไม่ใช่หลังจากที่ฉันทำตามใจตัวเอง
jp2code

24
ไม่จำเป็นต้องให้ความร่วมมือกับผู้โดยสารในการใช้อุปกรณ์ที่เหมาะสม (เช่นที่นั่งอีเจ็คเตอร์ที่มีเครื่องชั่งในตัว)
Jim Fred

คำตอบ:


102

วิธีหนึ่งคือใช้heap ขั้นต่ำ ( std::priority_queueใน C ++) นี่คือวิธีที่คุณทำโดยสมมติว่าคุณมีMinHeapชั้นเรียน (ใช่ตัวอย่างของฉันอยู่ใน C # ฉันคิดว่าคุณได้รับความคิด)

int targetTotal = 3000;
int totalWeight = 0;
// this creates an empty heap!
var myHeap = new MinHeap<Passenger>(/* need comparer here to order by weight */);
foreach (var pass in passengers)
{
    if (totalWeight < targetTotal)
    {
        // unconditionally add this passenger
        myHeap.Add(pass);
        totalWeight += pass.Weight;
    }
    else if (pass.Weight > myHeap.Peek().Weight)
    {
        // If this passenger is heavier than the lightest
        // passenger already on the heap,
        // then remove the lightest passenger and add this one
        var oldPass = myHeap.RemoveFirst();
        totalWeight -= oldPass.Weight;
        myHeap.Add(pass);
        totalWeight += pass.Weight;
    }
}

// At this point, the heaviest people are on the heap,
// but there might be too many of them.
// Remove the lighter people until we have the minimum necessary
while ((totalWeight - myHeap.Peek().Weight) > targetTotal)
{
    var oldPass = myHeap.RemoveFirst();
    totalWeight -= oldPass.Weight; 
}
// The heap now contains the passengers who will be thrown overboard.

ตามการอ้างอิงมาตรฐานเวลาทำงานควรเป็นสัดส่วนn log kซึ่งnเป็นจำนวนผู้โดยสารและkเป็นจำนวนสูงสุดของรายการในกอง หากเราคิดว่าน้ำหนักของผู้โดยสารโดยทั่วไปจะอยู่ที่ 100 ปอนด์หรือมากกว่านั้นก็ไม่น่าเป็นไปได้ที่ฮีปจะมีมากกว่า 30 รายการในเวลาใดก็ได้

กรณีที่เลวร้ายที่สุดคือถ้าผู้โดยสารแสดงตามลำดับจากน้ำหนักต่ำสุดไปสูงสุด ต้องมีการเพิ่มผู้โดยสารทุกคนในกองและผู้โดยสารทุกคนจะถูกลบออกจากกอง ยังคงมีผู้โดยสารนับล้านและสมมติว่าน้ำหนักที่เบาที่สุดมีน้ำหนัก 100 ปอนด์n log kผลงานออกมามีจำนวนน้อยพอสมควร

ถ้าคุณได้น้ำหนักของผู้โดยสารแบบสุ่มประสิทธิภาพจะดีขึ้นมาก ฉันใช้สิ่งนี้เป็นเครื่องมือแนะนำ (ฉันเลือก 200 รายการแรกจากรายการหลายล้านรายการ) ฉันมักจะจบลงด้วยการเพิ่มรายการจริงเพียง 50,000 หรือ 70,000 รายการในกอง

ฉันสงสัยว่าคุณจะเห็นสิ่งที่ค่อนข้างคล้ายกัน: ผู้สมัครส่วนใหญ่ของคุณจะถูกปฏิเสธเพราะพวกเขาเบากว่าคนที่เบาที่สุดในกองอยู่แล้ว และPeekเป็นการO(1)ผ่าตัด

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับผลการปฏิบัติงานของกองเลือกและรวดเร็วเลือกดูเมื่อทฤษฎีตรงกับการปฏิบัติ เวอร์ชั่นสั้น: หากคุณเลือกน้อยกว่า 1% ของจำนวนรายการทั้งหมดการเลือกฮีปคือผู้ชนะที่ชัดเจนกว่าการเลือกแบบด่วน มากกว่า 1% จากนั้นใช้อย่างรวดเร็วเลือกหรือตัวแปรเช่นIntroselect


1
SoapBox โพสต์คำตอบที่เร็วขึ้น
Mooing Duck

7
สำหรับการอ่านของฉันคำตอบของ SoapBox นั้นเทียบเท่ากับคำตอบทางศีลธรรมของคำตอบของ Jim Mischel SoapBox เขียนโค้ดของเขาใน C ++ และดังนั้นเขาจึงใช้ std :: set ซึ่งมีเวลาเพิ่ม log (N) เหมือนกับ MinHeap
IvyMike

1
มีวิธีแก้ปัญหาเวลาเชิงเส้น ฉันจะเพิ่ม
Neil G

2
มีคลาส STL สำหรับนาทีกอง:std::priority_queue
bdonlan

3
@MooingDuck: บางทีคุณเข้าใจผิด รหัสของฉันสร้างกองที่ว่างเปล่าเช่นเดียวกับรหัสของ SoapBox สร้างชุดที่ว่างเปล่า ความแตกต่างที่สำคัญอย่างที่ฉันเห็นคือรหัสของเขาจดจ้องชุดของน้ำหนักส่วนเกินเมื่อมีการเพิ่มรายการน้ำหนักที่สูงขึ้นในขณะที่ฉันเก็บรักษาส่วนที่เกิน ฉากของเขาจะลดขนาดลงเมื่อเขาเคลื่อนที่ผ่านรายการเพื่อค้นหาคนที่หนักกว่า ฮีปของฉันยังคงมีขนาดเท่ากันหลังจากถึงเกณฑ์น้ำหนักและฉันตัดมันหลังจากตรวจสอบรายการสุดท้ายในรายการ
Jim Mischel

119

อย่างไรก็ตามวิธีนี้จะไม่ช่วยแก้ปัญหาพร็อกซีของคุณ:

เพื่อให้ผู้โดยสาร 1,000,000 คนน้ำหนักลดลง 3,000 ปอนด์ผู้โดยสารแต่ละคนต้องสูญเสีย (3000/1000000) = 0.003 ปอนด์ต่อคน ที่สามารถทำได้โดยการ jettisoning ทุกคนเสื้อหรือรองเท้าหรือแม้กระทั่งคลิปเล็บมือช่วยทุกคน สิ่งนี้จะถือว่ามีการรวบรวมและโยนทิ้งที่มีประสิทธิภาพก่อนการลดน้ำหนักที่จำเป็นต้องเพิ่มขึ้นเนื่องจากเครื่องบินใช้เชื้อเพลิงมากขึ้น

ที่จริงแล้วพวกเขาไม่อนุญาตให้ใช้กรรไกรตัดเล็บบนกระดานอีกต่อไป


14
รักความสามารถในการตรวจสอบปัญหาและค้นหาวิธีที่ดีกว่าอย่างแท้จริง
fncomp

19
คุณคืออัจฉริยะ. :)
Jonathan

3
ฉันคิดว่ารองเท้าเพียงอย่างเดียวจะครอบคลุมสิ่งนี้
Mooing Duck

£ 0.003 คือ 0.048 ออนซ์ซึ่งอยู่ภายใต้ 1/20 ของออนซ์ ดังนั้นถ้าคนเพียงหนึ่งในหกสิบคนบนเครื่องบินกำลังใช้ประโยชน์จากกฎแชมพูสามออนซ์คุณสามารถประหยัดเวลาได้เพียงแค่โยนแชมพูทั้งหมดออกไป
Ryan Lundy

43

ด้านล่างเป็นการดำเนินการที่ค่อนข้างง่ายของโซลูชันที่ตรงไปตรงมา ฉันไม่คิดว่าจะมีวิธีที่เร็วกว่าที่ถูกต้อง 100%

size_t total = 0;
std::set<passenger> dead;
for ( auto p : passengers ) {
    if (dead.empty()) {
       dead.insert(p);
       total += p.weight;
       continue;
    }
    if (total < threshold || p.weight > dead.begin()->weight)
    {
        dead.insert(p);
        total += p.weight;
        while (total > threshold)
        {
            if (total - dead.begin()->weight < threshold)
                break;
            total -= dead.begin()->weight;
            dead.erase(dead.begin());
        }
    }
 }

มันทำงานได้โดยเติมชุดของ "คนตาย" จนกว่าจะถึงเกณฑ์ เมื่อถึงเกณฑ์ที่กำหนดเราจะผ่านรายการผู้โดยสารที่พยายามค้นหาสิ่งที่หนักกว่าคนที่เบาที่สุด เมื่อเราพบเจอเราเพิ่มเข้าไปในรายการแล้วเริ่ม "บันทึก" คนที่มีน้ำหนักเบาที่สุดในรายการจนกว่าเราจะไม่สามารถบันทึกได้อีก

ในกรณีที่เลวร้ายที่สุดสิ่งนี้จะทำงานเหมือนกับรายการทั้งหมด แต่ในกรณีที่ดีที่สุด (รายการ "ตาย" เต็มไปอย่างถูกต้องกับประชาชนครั้งแรก X) O(n)ก็จะดำเนินการ


1
ฉันคิดว่าคุณต้องอัปเดตtotalถัดจากcontinue; อื่น ๆ กว่านี้คือคำตอบที่ฉันจะโพสต์ ทางออกที่เร็วสุด
Mooing Duck

2
นี่คือคำตอบที่ถูกต้องนี่คือคำตอบที่เร็วที่สุดนี่คือคำตอบที่มีความซับซ้อนต่ำที่สุด
Xander Tulip

คุณอาจจะบีบเล็ก ๆ น้อย ๆ มากขึ้นออกมาจากมันโดยแคช dead.begin () และโดยการจัดเรียงสิ่งเล็กน้อยเพื่อลดการแตกแขนงซึ่งในโปรเซสเซอร์ที่ทันสมัยค่อนข้างช้า
Wug

dead.begin () มีแนวโน้มมากที่สุดและเกือบจะแน่นอนจะเป็นเพียงการเข้าถึงข้อมูล แต่ใช่แล้วการเคลื่อนไหวไปรอบ ๆ ifs บางอย่างจะทำให้ประสิทธิภาพเพิ่มขึ้นอีกเล็กน้อยด้วยการลดกิ่งไม้ ... แต่อาจมีค่าใช้จ่ายมากในการอ่าน
SoapBox

1
นี่คือเหตุผลทางตรรกะและตอบสนองความต้องการทั้งหมดของ OP รวมถึงไม่ทราบจำนวนผู้โดยสารล่วงหน้า แม้ว่าจะใช้เวลาเกือบ 5 เดือนในการทำงานกับแผนที่และชุด STL ฉันแน่ใจว่าการใช้ตัววนซ้ำอย่างกว้างขวางจะทำให้ประสิทธิภาพการทำงานแย่ลง เพียงเติมชุดและจากนั้นทำซ้ำจากขวาไปซ้ายจนกว่าผลรวมของคนที่หนักที่สุดคือมากกว่า 3,000 ชุดขององค์ประกอบ 1 ล้านรายการซึ่งเรียงลำดับแบบสุ่มจะโหลดที่ ~ 30 ล้าน / วินาทีบน i5 || i7 3.4Ghz แกน วนซ้ำอย่างน้อย 100X ช้า จูบจะชนะที่นี่
user2548100

32

สมมติว่าผู้โดยสารทุกคนจะให้ความร่วมมือ: ใช้เครือข่ายขนานเรียงลำดับ (ดูเพิ่มเติมที่นี่ )

นี่คือการสาธิตสด

อัปเดต: วิดีโอทางเลือก (ข้ามไปที่ 1:00)

การขอให้ผู้คนเปรียบเทียบกัน - คุณไม่สามารถทำได้เร็วกว่านี้


1
นี่คือการเรียงลำดับและจะเป็น O (nlogn) คุณสามารถทำงานได้เร็วขึ้นอย่างแน่นอนในฐานะที่เป็น O (nlogk) ซึ่ง k << n มีการแก้ปัญหา
Adam

1
@ อดัม: มันเรียงขนานกัน การเรียงลำดับมีขอบเขตล่างของ O (nlog n) ลำดับขั้นตอน อย่างไรก็ตามพวกมันสามารถขนานกันได้ดังนั้นความซับซ้อนของเวลาจึงน้อยลงมาก ดูตัวอย่างcs.umd.edu/~gasarch/ramsey/parasort.pdf
Lior Kogan

1
ทาง OP บอกว่า "นี่เป็นปัญหาพร็อกซีสำหรับบางสิ่งที่ฉันพยายามเขียนรหัสใน C ++" ดังนั้นแม้ว่าผู้โดยสารจะให้ความร่วมมือพวกเขาจะไม่คำนวณให้คุณ มันเป็นความคิดที่เรียบร้อย แต่ข้อสันนิษฐานของกระดาษที่คุณได้รับnโปรเซสเซอร์ไม่ได้ถือ
Adam

@LiorKogan - วิดีโอสาธิตสดไม่มีให้บริการบน youtube อีกต่อไป
Adelin

@Adelin: ขอบคุณเพิ่มวิดีโอทางเลือก
Lior Kogan

21

@ Blastfurnace อยู่ในเส้นทางที่ถูกต้อง คุณใช้ quickselect โดยที่ pivots เป็นขีด จำกัด น้ำหนัก แต่ละพาร์ติชันแยกคนหนึ่งชุดออกเป็นชุดและส่งคืนน้ำหนักรวมสำหรับแต่ละชุด คุณยังคงแบ่งกลุ่มที่เหมาะสมต่อไปจนกว่าถังของคุณที่สอดคล้องกับคนที่มีน้ำหนักมากที่สุดคือมากกว่า 3,000 ปอนด์และกลุ่มที่ต่ำที่สุดของคุณที่อยู่ในชุดนั้นมี 1 คน (นั่นคือมันไม่สามารถแยกได้อีกต่อไป)

อัลกอริทึมนี้เป็นเวลาเชิงเส้นตัดจำหน่าย แต่กรณีที่เลวร้ายที่สุดกำลังสอง ฉันคิดว่ามันเป็นอัลกอริธึมเชิงเส้นเวลาเท่านั้น


ต่อไปนี้เป็นโซลูชัน Python ที่แสดงอัลกอริทึมนี้:

#!/usr/bin/env python
import math
import numpy as np
import random

OVERWEIGHT = 3000.0
in_trouble = [math.floor(x * 10) / 10
              for x in np.random.standard_gamma(16.0, 100) * 8.0]
dead = []
spared = []

dead_weight = 0.0

while in_trouble:
    m = np.median(list(set(random.sample(in_trouble, min(len(in_trouble), 5)))))
    print("Partitioning with pivot:", m)
    lighter_partition = []
    heavier_partition = []
    heavier_partition_weight = 0.0
    in_trouble_is_indivisible = True
    for p in in_trouble:
        if p < m:
            lighter_partition.append(p)
        else:
            heavier_partition.append(p)
            heavier_partition_weight += p
        if p != m:
            in_trouble_is_indivisible = False
    if heavier_partition_weight + dead_weight >= OVERWEIGHT and not in_trouble_is_indivisible:
        spared += lighter_partition
        in_trouble = heavier_partition
    else:
        dead += heavier_partition
        dead_weight += heavier_partition_weight
        in_trouble = lighter_partition

print("weight of dead people: {}; spared people: {}".format(
    dead_weight, sum(spared)))
print("Dead: ", dead)
print("Spared: ", spared)

เอาท์พุท:

Partitioning with pivot: 121.2
Partitioning with pivot: 158.9
Partitioning with pivot: 168.8
Partitioning with pivot: 161.5
Partitioning with pivot: 159.7
Partitioning with pivot: 158.9
weight of dead people: 3051.7; spared people: 9551.7
Dead:  [179.1, 182.5, 179.2, 171.6, 169.9, 179.9, 168.8, 172.2, 169.9, 179.6, 164.4, 164.8, 161.5, 163.1, 165.7, 160.9, 159.7, 158.9]
Spared:  [82.2, 91.9, 94.7, 116.5, 108.2, 78.9, 83.1, 114.6, 87.7, 103.0, 106.0, 102.3, 104.9, 117.0, 96.7, 109.2, 98.0, 108.4, 99.0, 96.8, 90.7, 79.4, 101.7, 119.3, 87.2, 114.7, 90.0, 84.7, 83.5, 84.7, 111.0, 118.1, 112.1, 92.5, 100.9, 114.1, 114.7, 114.1, 113.7, 99.4, 79.3, 100.1, 82.6, 108.9, 103.5, 89.5, 121.8, 156.1, 121.4, 130.3, 157.4, 138.9, 143.0, 145.1, 125.1, 138.5, 143.8, 146.8, 140.1, 136.9, 123.1, 140.2, 153.6, 138.6, 146.5, 143.6, 130.8, 155.7, 128.9, 143.8, 124.0, 134.0, 145.0, 136.0, 121.2, 133.4, 144.0, 126.3, 127.0, 148.3, 144.9, 128.1]

3
+1 นี่เป็นแนวคิดที่น่าสนใจแม้ว่าฉันจะไม่แน่ใจว่ามันค่อนข้างเป็นเส้นตรง นอกจากว่าฉันขาดอะไรบางอย่างคุณจะต้องวนซ้ำสิ่งของเพื่อคำนวณน้ำหนักรวมของที่เก็บข้อมูลและคุณต้องคำนวณที่เก็บข้อมูลสูงอีกครั้ง (อย่างน้อยบางส่วน) ทุกครั้งที่คุณแยก มันจะยังเร็วกว่าวิธีการอิงฮีปของฉันในกรณีทั่วไป แต่ฉันคิดว่าคุณประเมินความซับซ้อนต่ำเกินไป
Jim Mischel

2
@ จิม: มันควรจะมีความซับซ้อนเช่นเดียวกับการเลือกอย่างรวดเร็ว ฉันรู้ว่าคำอธิบายเกี่ยวกับวิกิพีเดียนั้นไม่ดีที่สุด แต่เหตุผลที่ทำให้เวลาที่ใช้ในการตัดจำหน่ายแบบเชิงเส้นคือทุกครั้งที่คุณทำพาร์ติชันคุณทำงานกับพาร์ติชันเพียงด้านเดียว ไม่ใช่อย่างจริงจังจินตนาการว่าแต่ละพาร์ติชันแบ่งชุดของผู้คนในสอง จากนั้นขั้นตอนแรกจะใช้ O (n) จากนั้น O (n / 2) ฯลฯ และ n + n / 2 + n / 4 + ... = 2n
Neil G

2
@ จิม: อย่างไรก็ตามอัลกอริทึมของคุณมีเวลากรณีที่เลวร้ายที่สุดที่ดีที่สุดในขณะที่ฉันมีเวลากรณีเฉลี่ยที่ดีที่สุด ฉันคิดว่าพวกเขาทั้งคู่เป็นทางออกที่ดี
Neil G

2
@JimMischel, NeilG: codepad.org/FAx6hbtc ผมตรวจสอบแล้วทุกคนมีผลการเดียวกันและการแก้ไขของจิม FullSort: 1828 ticks JimMischel: 312 เห็บ SoapBox 109 เห็บ NeilG: 641 เห็บ
Mooing Duck

2
@NeilG: codepad.org/0KmcsvwD ผมใช้มาตรฐาน :: พาร์ทิชันที่จะทำให้การดำเนินงานของฉันของวิธีการขั้นตอนวิธีการของคุณเร็วขึ้น stdsort: 1812 เห็บ FullHeap 312 เห็บ Soapbox / JimMichel: 109 เห็บ, NeilG: 250 เห็บ
Mooing Duck

11

สมมติว่าเช่นเดียวกับน้ำหนักของผู้คนคุณมีความคิดที่ดีว่าค่าสูงสุดและต่ำสุดนั้นน่าจะใช้การเรียงลำดับแบบฐานเพื่อเรียงลำดับใน O (n) จากนั้นทำงานจากส่วนท้ายสุดของรายการไปทางที่เบาที่สุด เวลาทำงานทั้งหมด: O (n) น่าเสียดายที่ไม่มีการนำ Radix ไปใช้ใน STL แต่เป็นการเขียนที่ตรงไปตรงมา


ฉันจะไม่ใช้ Radix sort ทั่วไปเนื่องจากคุณไม่จำเป็นต้องเรียงลำดับรายการทั้งหมดเพื่อให้ได้คำตอบ
Mooing Duck

1
เพื่อความกระจ่างแจ้งการเรียงลำดับแบบ Radix เป็นความคิดที่ดี เพียงแค่ให้แน่ใจว่าได้เขียนการเพิ่มประสิทธิภาพที่กำหนดเอง
Mooing Duck

1
@Mooing: มันเป็นความจริงที่ว่าคุณไม่ต้องทำการเรียงลำดับของ radix ทั้งหมด แต่ในเวลาที่ฉันโพสต์สิ่งนี้ไม่มีอัลกอริทึม O (n) โพสต์และนี่เป็นวิธีที่ง่ายต่อการดู ฉันคิดว่าคำตอบของ Neil G คือเขาดีที่สุดในตอนนี้ที่เขาอธิบายอย่างเต็มที่และเริ่มใช้มัธยฐานให้เป็นเดือยสำหรับการเลือกของเขา แต่การใช้การเรียงลำดับมาตรฐานแบบธรรมดานั้นง่ายกว่าเล็กน้อยและมีโอกาสที่จะมีข้อบกพร่องเล็กน้อยในการใช้งานดังนั้นฉันจะปล่อยคำตอบ การจัดเรียง Radix บางส่วนที่ปรับแต่งเองนั้นจะเร็วขึ้นแน่นอน แต่จะไม่แสดงอาการ
Keith Irwin

6

ทำไมคุณไม่ใช้ quicksort บางส่วนกับกฎการยกเลิกที่แตกต่างจาก "sort" คุณสามารถเรียกใช้แล้วใช้เพียงครึ่งที่สูงกว่าและดำเนินต่อไปจนกว่าน้ำหนักภายในครึ่งที่สูงกว่านี้จะไม่มีน้ำหนักที่ถูกโยนออกไปอีกอย่างน้อยกว่าที่คุณย้อนกลับไปหนึ่งขั้นตอนในการเรียกซ้ำและเรียงลำดับรายการ หลังจากนั้นคุณสามารถเริ่มโยนคนออกจากรายการระดับสูงของรายการที่เรียงลำดับ


นั่นเป็นแนวคิดพื้นฐานที่อยู่เบื้องหลังขั้นตอนวิธีการนีลจีฉันคิดว่า
Mooing Duck

นั่นคือสาระสำคัญของการเลือกอย่างรวดเร็วซึ่งเป็นสิ่งที่ Neil G ใช้
Michael Donohue

6

จัดเรียงการแข่งขันแบบขนานใหญ่: -

สมมติว่ามีสามที่นั่งมาตรฐานในแต่ละด้านของโรค: -

  1. ขอให้ผู้โดยสารในที่นั่งริมหน้าต่างเพื่อเลื่อนไปยังที่นั่งตรงกลางหากพวกเขาหนักกว่าคนที่อยู่ในที่นั่งริมหน้าต่าง

  2. ขอให้ผู้โดยสารในที่นั่งตรงกลางเพื่อแลกเปลี่ยนกับผู้โดยสารในที่นั่งทางเดินถ้าพวกเขาหนัก

  3. ขอผู้โดยสารในที่นั่งทางเดินซ้ายเพื่อแลกเปลี่ยนกับผู้โดยสารในรหัสที่นั่งทางเดินที่ถูกต้องว่าหนักกว่า

  4. Bubble จัดเรียงผู้โดยสารในที่นั่งด้านขวาของทางเดิน (ทำขั้นตอน n ขั้นสำหรับ n แถว) - ถามผู้โดยสารในที่นั่งทางเดินขวาเพื่อแลกเปลี่ยนกับบุคคลที่อยู่ด้านหน้า n -1 ครั้ง

5 เตะพวกเขาออกไปที่ประตูจนกว่าจะถึง 3,000 ปอนด์

3 ขั้นตอน + ขั้นตอนบวก 30 ขั้นตอนหากคุณมีน้ำหนักผู้โดยสารที่ผอมมาก

สำหรับเครื่องบินเดินสองลำ - คำแนะนำนั้นซับซ้อนกว่า แต่ประสิทธิภาพก็ใกล้เคียงกัน


เช่นเดียวกับคำตอบของ Lior Kogan แต่รายละเอียดอื่น ๆ อีกมากมาย
Mooing Duck

7
วิธีแก้ปัญหา "ดีพอ" คือเสนอ "ฮ็อตด็อกฟรี" และโยนสิบห้าแรกที่มาถึงด้านหน้า จะไม่ให้ทางออกที่ดีที่สุดทุกครั้ง แต่ทำงานใน "O" ธรรมดา
James Anderson

มันจะเป็นการดีกว่าไหมที่จะโยน 15 รายการสุดท้ายเพราะคนที่หนักกว่าอาจจะช้ากว่านี้?
ปีเตอร์

@ Patriker - ฉันเชื่อว่าเป้าหมายคือการสูญเสีย 3,000 ปอนด์ด้วยจำนวนคนน้อยที่สุด แม้ว่าคุณจะสามารถปรับแต่งอัลกอริทึมได้โดยเปลี่ยนขั้นตอนที่ 4 เป็น "สลับกับคนในจาก n - 29 ครั้ง" ซึ่งจะได้ 30 pigiest ไปข้างหน้า แต่ไม่ใช่ในลำดับที่เข้มงวดของน้ำหนัก
James Anderson

4

ฉันอาจใช้std::nth_elementเพื่อแยกคนที่มีน้ำหนักมากที่สุด 20 คนออกเป็นเส้นตรง จากนั้นใช้วิธีการที่ซับซ้อนมากขึ้นในการค้นหาและชนจากสวรรค์ที่หนักที่สุด


3

คุณสามารถผ่านหนึ่งรายการเพื่อรับค่าเฉลี่ยและส่วนเบี่ยงเบนมาตรฐานจากนั้นใช้เพื่อประมาณจำนวนคนที่ต้องไป ใช้ partial_sort เพื่อสร้างรายการตามหมายเลขนั้น หากการเดาต่ำให้ใช้ partial_sort อีกครั้งในส่วนที่เหลือด้วยการเดาใหม่



2

ต่อไปนี้เป็นโซลูชันที่ใช้ฮีปโดยใช้โมดูลในตัวของไพ ธ อน อยู่ใน Python ดังนั้นจึงไม่ตอบคำถามดั้งเดิม แต่สะอาดกว่า (IMHO) มากกว่าโซลูชัน Python อื่น ๆ ที่โพสต์

import itertools, heapq

# Test data
from collections import namedtuple

Passenger = namedtuple("Passenger", "name seat weight")

passengers = [Passenger(*p) for p in (
    ("Alpha", "1A", 200),
    ("Bravo", "2B", 800),
    ("Charlie", "3C", 400),
    ("Delta", "4A", 300),
    ("Echo", "5B", 100),
    ("Foxtrot", "6F", 100),
    ("Golf", "7E", 200),
    ("Hotel", "8D", 250),
    ("India", "8D", 250),
    ("Juliet", "9D", 450),
    ("Kilo", "10D", 125),
    ("Lima", "11E", 110),
    )]

# Find the heaviest passengers, so long as their
# total weight does not exceeed 3000

to_toss = []
total_weight = 0.0

for passenger in passengers:
    weight = passenger.weight
    total_weight += weight
    heapq.heappush(to_toss, (weight, passenger))

    while total_weight - to_toss[0][0] >= 3000:
        weight, repreived_passenger = heapq.heappop(to_toss)
        total_weight -= weight


if total_weight < 3000:
    # Not enough people!
    raise Exception("We're all going to die!")

# List the ones to toss. (Order doesn't matter.)

print "We can get rid of", total_weight, "pounds"
for weight, passenger in to_toss:
    print "Toss {p.name!r} in seat {p.seat} (weighs {p.weight} pounds)".format(p=passenger)

หาก k = จำนวนผู้โดยสารที่จะโยนและ N = จำนวนผู้โดยสารกรณีที่ดีที่สุดสำหรับอัลกอริทึมนี้คือ O (N) และกรณีที่แย่ที่สุดสำหรับอัลกอริทึมนี้คือ Nlog (N) กรณีที่เลวร้ายที่สุดเกิดขึ้นถ้า k ใกล้ N เป็นเวลานาน นี่คือตัวอย่างของนักแสดงที่แย่ที่สุด:

weights = [2500] + [1/(2**n+0.0) for n in range(100000)] + [3000]

อย่างไรก็ตามในกรณีนี้ (โยนผู้คนออกจากเครื่องบิน (ด้วยร่มชูชีพฉันเข้าใจ)) จากนั้น k จะต้องน้อยกว่า 3000 ซึ่งก็คือ << "ผู้คนนับล้าน" รันไทม์เฉลี่ยควรอยู่ที่ประมาณ Nlog (k) ซึ่งเป็นเส้นตรงกับจำนวนคน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.