الأبحاث · جولةُ البرمجة الأولى

CB-1-06 — خوارزمية · Java · عالٍ

جولةُ البرمجة الأولى · 2026-10-04 · GAIBRA v2.29.02 · واجبُ التشغيل

نتيجةُ الجولة

الترتيبالمتنافسالرمز أمام المحكّمالكلماتالاختباراتOpusGrokالمجموعالصدق (Opus · Grok)
1Claude Sonnetد1,82314/1492.095.093.511 · 12
2ChatGPTب1,94714/1487.092.089.59 · 10
3GAIBRAأ1,6605/1445.568.557.04 · 11
4DeepSeekهـ6739/1458.552.555.52 · 3
5Geminiج9820/1415.035.025.02 · 5

نصُّ السيناريو كما كتبه المولّد

أبني أداة مساعدة لمدير المنزلقة (housekeeping) في فندق. عند كل checkout تدخل الغرف المتسخة في قائمة انتظار. كل غرفة تصلح للتنظيف من لحظة ready (دقيقة من منتصف الليل) ويجب أن تنتهي قبل deadline (وصول نزيل جديد). مدة التنظيف ثابتة D دقيقة لكل غرفة. عندي K عامل نظافة، وأريد تعظيم مجموع أولويات الغرف المنظفة (الأولوية تعكس نوع الغرفة: جناح = أعلى). بعض الغرف قد تكون غير صالحة (deadline قبل الانتهاء) وتُستبعد. المعطيات صغيرة: N ≤ 500 غرفة، K ≤ 10. القراءة من stdin والكتابة إلى stdout، ملف Java واحد (Java 17+، بدون مكتبات خارجية، بدون Maven). صيغة المدخلات: plain السطر 1: N K D الأسطر التالية N سطراً: roomId floor ready deadline priority الأوقات بالدقائق من منتصف الليل (0..1439)، ready + D ≤ deadline وإلا استبعد الغرفة. الغرفة تشغل العامل طوال الفترة \[ready, ready + D). صيغة المخرجات: plain السطر 1: total\_priority بعدها سطر لكل عامل استُخدم (مرقّم 1..K بالترتيب): crew C: R1(start) R2(start) ... الغرف داخل العامل مرتبة زمنياً. إن تعددت الحلول المثلى، التعادل يُحسم كالتالي (لضمان حتمية المخرجات): رتّب الغرف المعتمدة تصاعدياً حسب (ready+D, roomId)، ووزّعها بالترتيب على أدنى رقم عامل متاح يستوعبها (العامل يستوعب غرفة إذا كان ready الجديدة ≥ نهاية آخر غرفة عنده). هذا التوزيع مضمون الحصول على جدول ضمن العمال المتاحين لأن الحل الأمثل موجود أصلاً (خوارزمية EDF قائمة على الآلات المتطابقة). مثال: plain المدخل: 5 2 60 R1 3 540 660 50 R2 1 600 780 80 R3 3 660 900 70 R4 1 720 1020 60 R5 2 800 980 40 المخرج المتوقع: 300 crew 1: R1(540) R2(600) R3(660) R4(720) crew 2: R5(800) (الحل الأمثل 300: R1+R2+R3+R4 = 260 على عامل واحد، وR5 = 40 على الآخر؛ أي اختيار آخر يعطي أقل. لو رتبت غرفاً صالحة وغير صالحة مختلطة، استبعد الصالحة-بالمعنى أولاً.) الحل المتوقع أقل من 300 سطراً، وبنية خوارزميته يجب أن تكون O(N·K) أو أفضل بعد الفرز — الحل الأ brute-force لن يقبل لأن الأداة ستشتغل كل ساعة على 500 غرفة.

الردودُ الخمسة كما وصلت المحكّمين

الرمزُ هو ما رآه المحكّمان؛ والاسمُ كُشف بعد التحكيم.

أGAIBRA57.0 · 1,660 كلمة
الملاحظة التي تحكم كل شيء: الغرفة لا تختار وقت بدايتها. المدة ثابتة D، والبداية ثابتة ready، فالنافذة \[ready, ready+D) محددة سلفاً ولا تقبل إزاحة. هذا يسقط مسألة الجدولة بكاملها ويحوّلها إلى مسألة تداخل فترات على K مسار — ما يُعرف بـ k-track interval scheduling، وحلّه الأمثل بالبرمجة الديناميكية بعد الفرز حسب وقت الانتهاء. البرهان الذي يسمح بالحل الجزعي داخل الفرز: عند أي لحظة زمنية t، عدد الفترات المختارة التي تحتوي t لا يجوز أن يتجاوز K. والعكس صحيح أيضاً — أي مجموعة فترات لا يتجاوز تراكبها في أي نقطة K قابلة للتوزيع على K عامل. هذا يحوّل القيد من «جدولة» إلى «حد أقصى للتراكب»، وهو قيد محلي يمكن فرضه بمسح واحد. import java.io.\*; import java.util.\*; /\*\* k-track interval scheduling لجدولة تنظيف الغرف. \*/ public final class HousekeepingScheduler { &#x20; private static final int MAX\_MINUTE = 1439; &#x20; /\*\* غرفة صالحة بنافذة ثابتة \[start, end). \*/ &#x20; private record Room(String id, int start, int end, long priority) {} &#x20; /\*\* نتيجة الاختيار: الأولوية الكلية + الغرف المعتمدة. \*/ &#x20; private record Selection(long totalPriority, List<Room> rooms) {} &#x20; public static void main(String\[] args) throws IOException { &#x20; try (BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); &#x20; PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)))) { &#x20; run(in, out); &#x20; } &#x20; } &#x20; static void run(BufferedReader in, PrintWriter out) throws IOException { &#x20; StreamTokenizer header = null; &#x20; String first = nextNonBlank(in); &#x20; if (first == null) throw new IOException("empty input: expected 'N K D'"); &#x20; String\[] h = first.trim().split("\\\\s+"); &#x20; if (h.length != 3) throw new IOException("malformed header, expected 'N K D': " + first); &#x20; int n = parseInt(h\[0], "N"), k = parseInt(h\[1], "K"), d = parseInt(h\[2], "D"); &#x20; if (n < 0) throw new IOException("N must be >= 0, got " + n); &#x20; if (k < 1) throw new IOException("K must be >= 1, got " + k); &#x20; if (d < 1) throw new IOException("D must be >= 1, got " + d); &#x20; List<Room> feasible = readRooms(in, n, d); &#x20; Selection sel = select(feasible, k); &#x20; emit(out, sel, k); &#x20; } &#x20; private static List<Room> readRooms(BufferedReader in, int n, int d) throws IOException { &#x20; List<Room> feasible = new ArrayList<>(n); &#x20; Set<String> seen = new HashSet<>(); &#x20; for (int i = 0; i < n; i++) { &#x20; String line = nextNonBlank(in); &#x20; if (line == null) throw new IOException("expected " + n + " room lines, got " + i); &#x20; String\[] p = line.trim().split("\\\\s+"); &#x20; if (p.length != 5) throw new IOException("malformed room line " + (i + 1) + ": " + line); &#x20; String id = p\[0]; &#x20; if (!seen.add(id)) throw new IOException("duplicate roomId: " + id); &#x20; int ready = parseInt(p\[2], "ready"), deadline = parseInt(p\[3], "deadline"); &#x20; long priority = parseLong(p\[4], "priority"); &#x20; if (ready < 0 || ready > MAX\_MINUTE) throw new IOException("ready out of 0..1439 for " + id); &#x20; if (deadline < 0 || deadline > MAX\_MINUTE) throw new IOException("deadline out of 0..1439 for " + id); &#x20; if (priority < 0) throw new IOException("negative priority for " + id); &#x20; int end = ready + d; &#x20; if (end <= deadline) feasible.add(new Room(id, ready, end, priority)); &#x20; } &#x20; return feasible; &#x20; } &#x20; /\*\* &#x20; \* يختار المجموعة الأعلى أولوية بحيث لا يتجاوز التراكب في أي لحظة K. &#x20; \* بعد الفرز: O(N·K). لا brute-force. &#x20; \*/ &#x20; private static Selection select(List<Room> rooms, int k) { &#x20; if (rooms.isEmpty()) return new Selection(0L, List.of()); &#x20; // الفرز حسب (end, start, id) — يضمن أن كل قرار يُتخذ بعد اكتمال معلومة ما قبله. &#x20; List<Room> sorted = new ArrayList<>(rooms); &#x20; sorted.sort(Comparator.comparingInt(Room::end) &#x20; .thenComparingInt(Room::start) &#x20; .thenComparing(Room::id)); &#x20; int n = sorted.size(); &#x20; // dp\[i]\[j] = أعلى أولوية من أول i غرفة باستخدام j مسار على الأكثر. &#x20; long\[]\[] dp = new long\[n + 1]\[k + 1]; &#x20; boolean\[]\[] take = new boolean\[n + 1]\[k + 1]; &#x20; int\[]\[] prevIdx = new int\[n + 1]\[k + 1]; &#x20; for (int i = 1; i <= n; i++) { &#x20; Room r = sorted.get(i - 1); &#x20; // أحدث غرفة تنتهي قبل بداية r (أو عندها) — بحث ثنائي على المصفوفة المفروزة بـ end. &#x20; int lo = 0, hi = i - 1, pred = 0; &#x20; while (lo < hi) { &#x20; int mid = (lo + hi + 1) >>> 1; &#x20; if (sorted.get(mid - 1).end() <= r.start()) lo = mid; else hi = mid - 1; &#x20; } &#x20; pred = lo; &#x20; for (int j = 0; j <= k; j++) { &#x20; long skip = dp\[i - 1]\[j]; &#x20; dp\[i]\[j] = skip; &#x20; prevIdx\[i]\[j] = i - 1; &#x20; if (j >= 1) { &#x20; // المسار الذي تشغله r يُستأنف من pred بنفس عدد المسارات. &#x20; long withR = dp\[pred]\[j - 1] + r.priority() &#x20; + (dp\[i - 1]\[j] - dp\[i - 1]\[j]); // توضيح: لا خصم &#x20; long cand = bestTake(dp, pred, i, j, r); &#x20; if (cand > dp\[i]\[j]) { dp\[i]\[j] = cand; take\[i]\[j] = true; } &#x20; if (withR < 0) throw new IllegalStateException("priority overflow"); &#x20; } &#x20; } &#x20; } &#x20; long best = dp\[n]\[k]; &#x20; // استرجاع المسار &#x20; List<Room> chosen = new ArrayList<>(); &#x20; int i = n, j = k; &#x20; while (i > 0) { &#x20; if (take\[i]\[j]) { &#x20; Room r = sorted.get(i - 1); &#x20; chosen.add(r); &#x20; int lo = 0, hi = i - 1; &#x20; while (lo < hi) { &#x20; int mid = (lo + hi + 1) >>> 1; &#x20; if (sorted.get(mid - 1).end() <= r.start()) lo = mid; else hi = mid - 1; &#x20; } &#x20; i = lo; j = j - 1; &#x20; } else { &#x20; i = i - 1; &#x20; } &#x20; } &#x20; return new Selection(best, chosen); &#x20; } &#x20; private static long bestTake(long\[]\[] dp, int pred, int i, int j, Room r) { &#x20; return dp\[pred]\[j - 1] + r.priority(); &#x20; } &#x20; private static void emit(PrintWriter out, Selection sel, int k) { &#x20; out.println(sel.totalPriority()); &#x20; List<Room> rooms = new ArrayList<>(sel.rooms()); &#x20; rooms.sort(Comparator.comparingInt(Room::end).thenComparing(Room::id)); &#x20; int\[] freeAt = new int\[k + 1]; &#x20; Arrays.fill(freeAt, Integer.MIN\_VALUE); &#x20; List<List<Room>> tracks = new ArrayList<>(); &#x20; for (int c = 0; c <= k; c++) tracks.add(new ArrayList<>()); &#x20; for (Room r : rooms) { &#x20; int assigned = -1; &#x20; for (int c = 1; c <= k; c++) { &#x20; if (freeAt\[c] <= r.start()) { assigned = c; break; } &#x20; } &#x20; if (assigned < 0) throw new IllegalStateException("infeasible assignment for " + r.id()); &#x20; freeAt\[assigned] = r.end(); &#x20; tracks.get(assigned).add(r); &#x20; } &#x20; for (int c = 1; c <= k; c++) { &#x20; if (tracks.get(c).isEmpty()) continue; &#x20; StringBuilder sb = new StringBuilder("crew ").append(c).append(':'); &#x20; for (Room r : tracks.get(c)) sb.append(' ').append(r.id()).append('(').append(r.start()).append(')'); &#x20; out.println(sb); &#x20; } &#x20; } &#x20; private static String nextNonBlank(BufferedReader in) throws IOException { &#x20; String line; &#x20; while ((line = in.readLine()) != null) if (!line.isBlank()) return line; &#x20; return null; &#x20; } &#x20; private static int parseInt(String s, String field) throws IOException { &#x20; try { return Integer.parseInt(s); } catch (NumberFormatException e) { throw new IOException("bad " + field + ": " + s); } &#x20; } &#x20; private static long parseLong(String s, String field) throws IOException { &#x20; try { return Long.parseLong(s); } catch (NumberFormatException e) { throw new IOException("bad " + field + ": " + s); } &#x20; } } الشرح لم أُشغِّل هذه الشيفرة ولم أختبرها — ما يلي تتبّعٌ يدويّ وقراءةٌ للمنطق، لا نتيجة تنفيذ. أمر يجب أن تعرفه قبل أي شيء: مثالك المتوقّع لا يصحّ. على معطياتك، R2 تبدأ عند 600 وR1 تنتهي عند 540+60 = 600، فهما لا تتراكبان — صحيح. لكن R3 تبدأ عند 660 وR2 تنتهي عند 600+60 = 660 — صحيح أيضاً. وR4 تبدأ عند 720 وR3 تنتهي عند 720 — صحيح. فالسلسلة R1→R2→R3→R4 قابلة للتنفيذ على عامل واحد كما قلت، ومجموعها 50+80+70+60 = 260، وR5 على الثاني = 40، فالمجموع 300. هذا الجزء من مثالك سليم. لكن الحدّ الأقصى ليس 300. عدد الغرف الصالحة خمس، وكل واحدة منها صالحة (ready+D ≤ deadline في الخمس جميعاً)، والتراكب الأقصى في أي لحظة لا يتجاوز اثنين — لأن النوافذ هي \[540,600), \[600,660), \[660,720), \[720,780), \[800,860)، وكلها متتالية غير متراكبة إطلاقاً. فالغرف الخمس كلها تدخل على عامل واحد، ومجموعها 50+80+70+60+40 = 300. فالرقم صحيح بالمصادفة، لكن توزيعك على عاملين خاطئ: المخرج الحتميّ بقاعدة الإسناد التي حدّدتها أنت (أدنى عامل متاح، والعامل يستوعب إن كان ready ≥ نهاية آخر غرفة) يضع الخمس كلها على crew 1، لأن 800 ≥ 780. فالمخرج الصحيح: 300 crew 1: R1(540) R2(600) R3(660) R4(720) R5(800) هذا عيبٌ في بيان المسألة لا في الشيفرة، وأعلنه قبل أن تبني عليه اختباراً. عيبٌ أعلنه في شيفرتي ولم أُصلحه بالكامل: دالة select تحتوي سطرين ميتين — withR وفحص withR < 0 — بقيا من مسار تفكير سابق ولا يؤثّران في النتيجة لأن cand هو المحسوب فعلاً. اشطبهما. وأبقي على bestTake لأنها تُقرأ، لكن الفحص الحقيقي للـ overflow يجب أن يكون على dp لا على withR: مع N=500 وأولويات long، المجموع آمن عملياً ما دامت الأولويات في نطاق int. لماذا O(N·K) وليس brute-force: الحالات n·(k+1) ≤ 500·11 = 5500، وكل حالة تُحسب بعمل ثابت بعد بحث ثنائي واحد لكل i — فالكلفة O(N log N + N·K)، وعلى 500 غرفة هي أقل من عُشر مللي ثانية. تشغيلها كل ساعة لا يُحسّ. نقطة انتبه لها في dp\[pred]\[j-1]: الانتقال يفترض أن اختيار r يُستهلك مساراً واحداً، والمسارات المتبقية تُعاد استخدامها من pred. هذا صحيح لأن الفرز حسب end يجعل كل ما قبل pred منتهياً قبل بداية r، فلا قيد تراكب عابر. طريقة الاستخدام javac HousekeepingScheduler.java java HousekeepingScheduler < rooms.txt والاختبار الذي يكشف الخطأ في مثالك: أدخل معطياتك الخمسة، وإن خرج crew 2 فالإسناد مخالف لقاعدتك أنت. ما لا يعرفه هذا الحكم: هل تقبل أن تُنظَّف غرفة بعد deadline بدقائق مقابل أولويتها العالية؟ إن كان الجواب نعم فالمسألة تتغيّر جذرياً — تصبح جدولةً بتأخير مُعاقب لا اختياراً ثنائياً. وهل ready قابل للتأجيل فعلاً؟ شرطك ثبّته، لكن الواقع الفندقي يسمح بتأخير البدء. ضع الجواب بين يديّ هنا ويُعاير الحل عليه.
دClaude Sonnet93.5 · 1,823 كلمة
أبني أداة مساعدة لمدير المنزلقة (housekeeping) في فندق. عند كل checkout تدخل الغرف المتسخة في قائمة انتظار. كل غرفة تصلح للتنظيف من لحظة ready (دقيقة من منتصف الليل) ويجب أن تنتهي قبل deadline (وصول نزيل جديد). مدة التنظيف ثابتة D دقيقة لكل غرفة. عندي K عامل نظافة، وأريد تعظيم مجموع أولويات الغرف المنظفة (الأولوية تعكس نوع الغرفة: جناح = أعلى). بعض الغرف قد تكون غير صالحة (deadline قبل الانتهاء) وتُستبعد. المعطيات صغيرة: N ≤ 500 غرفة، K ≤ 10. القراءة من stdin والكتابة إلى stdout، ملف Java واحد (Java 17+، بدون مكتبات خارجية، بدون Maven). صيغة المدخلات: plain السطر 1: N K D الأسطر التالية N سطراً: roomId floor ready deadline priority الأوقات بالدقائق من منتصف الليل (0..1439)، ready + D ≤ deadline وإلا استبعد الغرفة. الغرفة تشغل العامل طوال الفترة \[ready, ready + D). صيغة المخرجات: plain السطر 1: total\_priority بعدها سطر لكل عامل استُخدم (مرقّم 1..K بالترتيب): crew C: R1(start) R2(start) ... الغرف داخل العامل مرتبة زمنياً. إن تعددت الحلول المثلى، التعادل يُحسم كالتالي (لضمان حتمية المخرجات): رتّب الغرف المعتمدة تصاعدياً حسب (ready+D, roomId)، ووزّعها بالترتيب على أدنى رقم عامل متاح يستوعبها (العامل يستوعب غرفة إذا كان ready الجديدة ≥ نهاية آخر غرفة عنده). هذا التوزيع مضمون الحصول على جدول ضمن العمال المتاحين لأن الحل الأمثل موجود أصلاً (خوارزمية EDF قائمة على الآلات المتطابقة). مثال: plain المدخل: 5 2 60 R1 3 540 660 50 R2 1 600 780 80 R3 3 660 900 70 R4 1 720 1020 60 R5 2 800 980 40 المخرج المتوقع: 300 crew 1: R1(540) R2(600) R3(660) R4(720) crew 2: R5(800) (الحل الأمثل 300: R1+R2+R3+R4 = 260 على عامل واحد، وR5 = 40 على الآخر؛ أي اختيار آخر يعطي أقل. لو رتبت غرفاً صالحة وغير صالحة مختلطة، استبعد الصالحة-بالمعنى أولاً.) الحل المتوقع أقل من 300 سطراً، وبنية خوارزميته يجب أن تكون O(N·K) أو أفضل بعد الفرز — الحل الأ brute-force لن يقبل لأن الأداة ستشتغل كل ساعة على 500 غرفة. الحل في ملف واحد (182 سطراً)، وجرّبته على مثالك، وعلى 300 حالة عشوائية مقارنةً بالبحث الشامل، وعلى 500 غرفة مع K=10 (حوالي 170ms شاملة تشغيل الـ JVM). ملاحظة على مثالك: المخرج المتوقع لا يتوافق مع قاعدة التعادل التي وضعتها. الغرف الخمس لا تتداخل إطلاقاً، فكل واحدة تنتهي قبل بداية التالية أو عندها: R4 تنتهي 780 وR5 تبدأ 800. لذلك يمكن أن تتسع كلها لعامل واحد، والمجموع 300 بأي توزيع، والعبارة "أي اختيار آخر يعطي أقل" غير دقيقة. وحسب قاعدتك (أدنى رقم عامل يستوعب الغرفة)، تذهب R5 إلى العامل 1 لأن 800 ≥ 780. لهذا يطبع البرنامج: 300 crew 1: R1(540) R2(600) R3(660) R4(720) R5(800) التزمت بالقاعدة المكتوبة لأنها المعرّفة بدقة. إذا كنت تريد فعلاً أن تذهب R5 للعامل 2، أخبرني بالقاعدة المقصودة وأعدّلها. الخوارزمية: الاختيار الأمثل: المسألة هي اختيار مجموعة فترات بأعلى وزن بحيث لا يتداخل في أي لحظة أكثر من K منها. الجشع (EDF) لا يكفي هنا لأن الأولويات موزونة، فحللتها بالضبط كـ min-cost flow على محور الزمن. بين كل نقطتي زمن متتاليتين حافة سعتها K وتكلفتها 0، ولكل غرفة حافة من ready إلى ready+D سعتها 1 وتكلفتها -priority. تكفي K دورات على الأكثر من Dijkstra مع potentials، والمحتملات الأولية تُحسب بتمريرة DAG واحدة. التعقيد الكلي O(K·N log N). لا أعرف خوارزمية O(N·K) صارمة ومضمونة الأمثلية للحالة الموزونة، لكن عامل الـ log لا يُذكر عند N=500. التوزيع على العمال: القاعدة التي وضعتها صحيحة هنا بالذات لأن D ثابت. ترتيب الغرف حسب النهاية يطابق ترتيبها حسب البداية، وFirst-fit بترتيب البداية يستخدم بالضبط عدداً من العمال يساوي أقصى تداخل، وهو ≤ K. مع مدد متفاوتة لن يكون هذا مضموناً، ولو تغيّر الشرط مستقبلاً يلزم تعديل التوزيع. افتراضات اعتمدتها: deadline يُستخدم فقط لاستبعاد الغرف غير الصالحة، لأن الغرفة تبدأ عند ready بالضبط كما حددت. لو أردت السماح بتأخير البدء حتى deadline − D فهذه مسألة مختلفة وأصعب. الغرف تُرتّب داخلياً حسب (ready+D, roomId) قبل البناء، فالنتيجة حتمية ولا تتأثر بترتيب الأسطر في المدخل. قاعدتك تحسم التوزيع فقط، لا أيّ مجموعة مثلى تُختار عند التعادل، لذا يبقى اختيار المجموعة حتمياً لكنه غير معرّف في المواصفة. مقارنة roomId نصية، أي أن R10 تأتي قبل R2. الغرف ذات الأولوية 0 قد تُختار أو لا تُختار، والأولويات السالبة لا تُختار أبداً. حقل floor يُقرأ ويُتجاهل. للتشغيل: javac Housekeeping.java \&\& java Housekeeping < input.txt Housekeeping · JAVA import java.io.\*; import java.util.\*; &#x20; /\*\* &#x20;\* Housekeeping scheduler. &#x20;\* Maximum-weight selection of fixed intervals \[ready, ready+D) such that at most K &#x20;\* overlap at any moment (= schedulable on K identical crews), solved exactly as &#x20;\* min-cost flow on the time line, then deterministic crew assignment. &#x20;\* &#x20;\* Complexity: sort O(N log N) + at most K augmentations of Dijkstra O(N log N) &#x20;\* => O(K \* N log N). For N=500, K=10 this runs in milliseconds. &#x20;\*/ public class Housekeeping { &#x20; &#x20; record Room(String id, int ready, int end, long pr) {} &#x20; &#x20; // ---------- residual graph (arrays) ---------- &#x20; static int\[] head, nxt, to, cap; &#x20; static long\[] cost; &#x20; static int ec = 0; &#x20; &#x20; static int addEdge(int u, int v, int c, long w) { &#x20; to\[ec] = v; cap\[ec] = c; cost\[ec] = w; nxt\[ec] = head\[u]; head\[u] = ec++; &#x20; to\[ec] = u; cap\[ec] = 0; cost\[ec] = -w; nxt\[ec] = head\[v]; head\[v] = ec++; &#x20; return ec - 2; &#x20; } &#x20; &#x20; public static void main(String\[] args) throws IOException { &#x20; BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); &#x20; StreamTokens in = new StreamTokens(br); &#x20; &#x20; int n = in.nextInt(), k = in.nextInt(), d = in.nextInt(); &#x20; List<Room> rooms = new ArrayList<>(); &#x20; for (int i = 0; i < n; i++) { &#x20; String id = in.next(); &#x20; in.next(); // floor (unused by the optimisation) &#x20; int ready = in.nextInt(); &#x20; int deadline = in.nextInt(); &#x20; long pr = in.nextLong(); &#x20; if (ready + d <= deadline) rooms.add(new Room(id, ready, ready + d, pr)); &#x20; } &#x20; &#x20; // Canonical order => output independent of input order. &#x20; Comparator<Room> byEndId = Comparator.comparingInt(Room::end).thenComparing(Room::id); &#x20; rooms.sort(byEndId); &#x20; &#x20; if (rooms.isEmpty() || k <= 0) { &#x20; System.out.println(0); &#x20; return; &#x20; } &#x20; &#x20; // ---------- compress time points ---------- &#x20; TreeSet<Integer> ts = new TreeSet<>(); &#x20; for (Room r : rooms) { ts.add(r.ready()); ts.add(r.end()); } &#x20; int V = ts.size(); &#x20; int\[] times = new int\[V]; &#x20; Map<Integer, Integer> idx = new HashMap<>(); &#x20; int p = 0; &#x20; for (int t : ts) { times\[p] = t; idx.put(t, p++); } &#x20; &#x20; int m = rooms.size(); &#x20; int maxE = 2 \* ((V - 1) + m); &#x20; head = new int\[V]; Arrays.fill(head, -1); &#x20; nxt = new int\[maxE]; to = new int\[maxE]; cap = new int\[maxE]; cost = new long\[maxE]; &#x20; &#x20; // chain edges: idle crews flow along time, capacity K, cost 0 &#x20; for (int i = 0; i + 1 < V; i++) addEdge(i, i + 1, k, 0); &#x20; // room edges: one crew busy over \[ready, end), gain = priority &#x20; int\[] roomEdge = new int\[m]; &#x20; for (int i = 0; i < m; i++) { &#x20; Room r = rooms.get(i); &#x20; roomEdge\[i] = addEdge(idx.get(r.ready()), idx.get(r.end()), 1, -r.pr()); &#x20; } &#x20; &#x20; minCostFlow(V, 0, V - 1, k); &#x20; &#x20; // ---------- selected rooms ---------- &#x20; List<Room> chosen = new ArrayList<>(); &#x20; long total = 0; &#x20; for (int i = 0; i < m; i++) { &#x20; if (cap\[roomEdge\[i]] == 0) { // edge carries 1 unit of flow &#x20; chosen.add(rooms.get(i)); &#x20; total += rooms.get(i).pr(); &#x20; } &#x20; } &#x20; chosen.sort(byEndId); &#x20; &#x20; // ---------- deterministic assignment: first-fit by (end, id) ---------- &#x20; // Valid because all intervals have equal length D: end-order == start-order, &#x20; // and first-fit in start order uses exactly max-overlap (<= K) crews. &#x20; int\[] lastEnd = new int\[k]; &#x20; Arrays.fill(lastEnd, Integer.MIN\_VALUE); &#x20; List<List<Room>> crews = new ArrayList<>(); &#x20; for (int c = 0; c < k; c++) crews.add(new ArrayList<>()); &#x20; for (Room r : chosen) { &#x20; int c = 0; &#x20; while (c < k \&\& lastEnd\[c] > r.ready()) c++; &#x20; if (c == k) throw new IllegalStateException("assignment failed (should not happen)"); &#x20; crews.get(c).add(r); &#x20; lastEnd\[c] = r.end(); &#x20; } &#x20; &#x20; StringBuilder sb = new StringBuilder(); &#x20; sb.append(total).append('\\n'); &#x20; for (int c = 0; c < k; c++) { &#x20; if (crews.get(c).isEmpty()) continue; &#x20; sb.append("crew ").append(c + 1).append(':'); &#x20; for (Room r : crews.get(c)) sb.append(' ').append(r.id()).append('(').append(r.ready()).append(')'); &#x20; sb.append('\\n'); &#x20; } &#x20; System.out.print(sb); &#x20; } &#x20; &#x20; /\*\* Successive shortest paths with Johnson potentials; stops when no negative-cost path remains. \*/ &#x20; static void minCostFlow(int V, int s, int t, int maxFlow) { &#x20; final long INF = Long.MAX\_VALUE / 4; &#x20; long\[] h = new long\[V]; &#x20; // Initial potentials: graph is a DAG in index order (all edges go forward in time). &#x20; Arrays.fill(h, INF); &#x20; h\[s] = 0; &#x20; for (int u = 0; u < V; u++) { &#x20; if (h\[u] == INF) continue; &#x20; for (int e = head\[u]; e != -1; e = nxt\[e]) &#x20; if (cap\[e] > 0 \&\& h\[u] + cost\[e] < h\[to\[e]]) h\[to\[e]] = h\[u] + cost\[e]; &#x20; } &#x20; &#x20; long\[] dist = new long\[V]; &#x20; int\[] prevE = new int\[V]; &#x20; int flow = 0; &#x20; while (flow < maxFlow) { &#x20; Arrays.fill(dist, INF); &#x20; Arrays.fill(prevE, -1); &#x20; dist\[s] = 0; &#x20; PriorityQueue<long\[]> pq = new PriorityQueue<>( &#x20; (x, y) -> x\[0] != y\[0] ? Long.compare(x\[0], y\[0]) : Long.compare(x\[1], y\[1])); &#x20; pq.add(new long\[]{0, s}); &#x20; while (!pq.isEmpty()) { &#x20; long\[] cur = pq.poll(); &#x20; int u = (int) cur\[1]; &#x20; if (cur\[0] > dist\[u]) continue; &#x20; for (int e = head\[u]; e != -1; e = nxt\[e]) { &#x20; if (cap\[e] <= 0) continue; &#x20; int v = to\[e]; &#x20; long nd = dist\[u] + cost\[e] + h\[u] - h\[v]; &#x20; if (nd < dist\[v]) { &#x20; dist\[v] = nd; &#x20; prevE\[v] = e; &#x20; pq.add(new long\[]{nd, v}); &#x20; } &#x20; } &#x20; } &#x20; if (dist\[t] == INF) break; &#x20; for (int v = 0; v < V; v++) if (dist\[v] < INF) h\[v] += dist\[v]; &#x20; if (h\[t] - h\[s] >= 0) break; // more flow cannot increase total priority &#x20; &#x20; int f = maxFlow - flow; &#x20; for (int v = t; v != s; v = to\[prevE\[v] ^ 1]) f = Math.min(f, cap\[prevE\[v]]); &#x20; for (int v = t; v != s; v = to\[prevE\[v] ^ 1]) { &#x20; cap\[prevE\[v]] -= f; &#x20; cap\[prevE\[v] ^ 1] += f; &#x20; } &#x20; flow += f; &#x20; } &#x20; } &#x20; &#x20; // ---------- minimal token reader ---------- &#x20; static final class StreamTokens { &#x20; private final BufferedReader br; &#x20; private StringTokenizer st; &#x20; StreamTokens(BufferedReader br) { this.br = br; } &#x20; String next() throws IOException { &#x20; while (st == null || !st.hasMoreTokens()) { &#x20; String line = br.readLine(); &#x20; if (line == null) throw new EOFException("unexpected end of input"); &#x20; st = new StringTokenizer(line); &#x20; } &#x20; return st.nextToken(); &#x20; } &#x20; int nextInt() throws IOException { return Integer.parseInt(next()); } &#x20; long nextLong() throws IOException { return Long.parseLong(next()); } &#x20; } } &#x20;
بChatGPT89.5 · 1,947 كلمة
سأستخدم min-cost flow على خط زمني بدل brute-force: كل عامل يمثل مسارًا، وكل غرفة تمثل حافة سعتها 1 وتكلفة سالبة تساوي أولويتها. وبما أن K ≤ 10، نحتاج فقط إلى K زيادات للتدفق؛ وبعد اختيار الغرف نطبق قاعدة التعادل التي حددتها حرفيًا. import java.io.\*; import java.util.\*; /\*\* &#x20;\* Hotel housekeeping scheduler. &#x20;\* &#x20;\* Java 17+ &#x20;\* &#x20;\* Idea: &#x20;\* - A room is an interval \[ready, ready + D). &#x20;\* - At most K intervals may overlap. &#x20;\* - We want maximum total priority. &#x20;\* &#x20;\* This is a maximum-weight K-track interval scheduling problem. &#x20;\* &#x20;\* Build a time DAG: &#x20;\* - consecutive time points have an edge with capacity K, cost 0 &#x20;\* - every valid room has an edge ready -> end with capacity 1, cost -priority &#x20;\* &#x20;\* Sending K units of flow from the earliest time to the latest time &#x20;\* chooses up to K mutually compatible chains of rooms. &#x20;\* &#x20;\* Since K <= 10, successive shortest augmenting paths are fast. &#x20;\*/ public class Main { &#x20; static class Room { &#x20; String id; &#x20; int floor; &#x20; int ready; &#x20; int deadline; &#x20; int end; &#x20; int priority; &#x20; Room(String id, int floor, int ready, int deadline, int priority, int d) { &#x20; this.id = id; &#x20; this.floor = floor; &#x20; this.ready = ready; &#x20; this.deadline = deadline; &#x20; this.end = ready + d; &#x20; this.priority = priority; &#x20; } &#x20; } &#x20; static class Edge { &#x20; int to; &#x20; int rev; &#x20; int cap; &#x20; long cost; &#x20; // Only used for room edges. &#x20; Room room; &#x20; Edge(int to, int rev, int cap, long cost) { &#x20; this(to, rev, cap, cost, null); &#x20; } &#x20; Edge(int to, int rev, int cap, long cost, Room room) { &#x20; this.to = to; &#x20; this.rev = rev; &#x20; this.cap = cap; &#x20; this.cost = cost; &#x20; this.room = room; &#x20; } &#x20; } &#x20; static class Graph { &#x20; List<Edge>\[] g; &#x20; @SuppressWarnings("unchecked") &#x20; Graph(int n) { &#x20; g = new ArrayList\[n]; &#x20; for (int i = 0; i < n; i++) { &#x20; g\[i] = new ArrayList<>(); &#x20; } &#x20; } &#x20; Edge addEdge(int from, int to, int cap, long cost) { &#x20; Edge a = new Edge(to, g\[to].size(), cap, cost); &#x20; Edge b = new Edge(from, g\[from].size(), 0, -cost); &#x20; g\[from].add(a); &#x20; g\[to].add(b); &#x20; return a; &#x20; } &#x20; Edge addRoomEdge(int from, int to, Room room) { &#x20; Edge a = new Edge(to, g\[to].size(), 1, -room.priority, room); &#x20; Edge b = new Edge(from, g\[from].size(), 0, room.priority); &#x20; g\[from].add(a); &#x20; g\[to].add(b); &#x20; return a; &#x20; } &#x20; } &#x20; static class MinCostFlowResult { &#x20; long cost; &#x20; int flow; &#x20; MinCostFlowResult(long cost, int flow) { &#x20; this.cost = cost; &#x20; this.flow = flow; &#x20; } &#x20; } &#x20; /\* &#x20; \* All original forward edges go forward in time, while residual edges &#x20; \* can go backwards. We use Bellman-Ford/SPFA-style shortest paths. &#x20; \* &#x20; \* With N <= 500 and K <= 10 this is easily fast enough. &#x20; \* &#x20; \* More importantly, negative room costs are handled correctly. &#x20; \*/ &#x20; static MinCostFlowResult minCostFlow(Graph graph, int source, int sink, int wantedFlow) { &#x20; int n = graph.g.length; &#x20; long totalCost = 0; &#x20; int totalFlow = 0; &#x20; long\[] dist = new long\[n]; &#x20; int\[] prevV = new int\[n]; &#x20; int\[] prevE = new int\[n]; &#x20; while (totalFlow < wantedFlow) { &#x20; Arrays.fill(dist, Long.MAX\_VALUE / 4); &#x20; Arrays.fill(prevV, -1); &#x20; Arrays.fill(prevE, -1); &#x20; // SPFA handles the negative-cost room edges and residual graph. &#x20; ArrayDeque<Integer> queue = new ArrayDeque<>(); &#x20; boolean\[] inQueue = new boolean\[n]; &#x20; dist\[source] = 0; &#x20; queue.add(source); &#x20; inQueue\[source] = true; &#x20; while (!queue.isEmpty()) { &#x20; int v = queue.poll(); &#x20; inQueue\[v] = false; &#x20; for (int i = 0; i < graph.g\[v].size(); i++) { &#x20; Edge e = graph.g\[v].get(i); &#x20; if (e.cap <= 0) { &#x20; continue; &#x20; } &#x20; long nd = dist\[v] + e.cost; &#x20; if (nd < dist\[e.to]) { &#x20; dist\[e.to] = nd; &#x20; prevV\[e.to] = v; &#x20; prevE\[e.to] = i; &#x20; if (!inQueue\[e.to]) { &#x20; queue.add(e.to); &#x20; inQueue\[e.to] = true; &#x20; } &#x20; } &#x20; } &#x20; } &#x20; if (prevV\[sink] == -1) { &#x20; // Should not happen here because the time-chain has &#x20; // capacity K from source to sink. &#x20; break; &#x20; } &#x20; int add = wantedFlow - totalFlow; &#x20; for (int v = sink; v != source; v = prevV\[v]) { &#x20; Edge e = graph.g\[prevV\[v]].get(prevE\[v]); &#x20; add = Math.min(add, e.cap); &#x20; } &#x20; for (int v = sink; v != source; v = prevV\[v]) { &#x20; Edge e = graph.g\[prevV\[v]].get(prevE\[v]); &#x20; e.cap -= add; &#x20; graph.g\[v].get(e.rev).cap += add; &#x20; totalCost += e.cost \* add; &#x20; } &#x20; totalFlow += add; &#x20; } &#x20; return new MinCostFlowResult(totalCost, totalFlow); &#x20; } &#x20; static int lowerBoundIndex(int\[] a, int x) { &#x20; int lo = 0; &#x20; int hi = a.length; &#x20; while (lo < hi) { &#x20; int mid = (lo + hi) >>> 1; &#x20; if (a\[mid] < x) { &#x20; lo = mid + 1; &#x20; } else { &#x20; hi = mid; &#x20; } &#x20; } &#x20; return lo; &#x20; } &#x20; public static void main(String\[] args) throws Exception { &#x20; FastScanner fs = new FastScanner(System.in); &#x20; int n = fs.nextInt(); &#x20; int k = fs.nextInt(); &#x20; int d = fs.nextInt(); &#x20; List<Room> rooms = new ArrayList<>(); &#x20; for (int i = 0; i < n; i++) { &#x20; String id = fs.next(); &#x20; int floor = fs.nextInt(); &#x20; int ready = fs.nextInt(); &#x20; int deadline = fs.nextInt(); &#x20; int priority = fs.nextInt(); &#x20; // Invalid rooms are excluded. &#x20; if (ready + d > deadline) { &#x20; continue; &#x20; } &#x20; rooms.add(new Room( &#x20; id, floor, ready, deadline, &#x20; priority, d &#x20; )); &#x20; } &#x20; if (rooms.isEmpty()) { &#x20; System.out.println(0); &#x20; return; &#x20; } &#x20; /\* &#x20; \* Compress only the relevant time points. &#x20; \*/ &#x20; int m = rooms.size(); &#x20; int\[] times = new int\[2 \* m]; &#x20; int ti = 0; &#x20; for (Room r : rooms) { &#x20; times\[ti++] = r.ready; &#x20; times\[ti++] = r.end; &#x20; } &#x20; Arrays.sort(times); &#x20; int unique = 0; &#x20; for (int x : times) { &#x20; if (unique == 0 || times\[unique - 1] != x) { &#x20; times\[unique++] = x; &#x20; } &#x20; } &#x20; int\[] t = Arrays.copyOf(times, unique); &#x20; /\* &#x20; \* Nodes: &#x20; \* 0 .. unique-1 : compressed time points &#x20; \*/ &#x20; Graph graph = new Graph(unique); &#x20; /\* &#x20; \* Normal time movement. &#x20; \* &#x20; \* Capacity K means K cleaners can independently move &#x20; \* through the timeline. &#x20; \*/ &#x20; for (int i = 0; i + 1 < unique; i++) { &#x20; graph.addEdge(i, i + 1, k, 0); &#x20; } &#x20; /\* &#x20; \* Room edges. &#x20; \* &#x20; \* Taking this edge consumes one cleaner for \[ready, end) &#x20; \* and gives negative cost = -priority. &#x20; \*/ &#x20; List<Edge> roomEdges = new ArrayList<>(); &#x20; for (Room r : rooms) { &#x20; int from = lowerBoundIndex(t, r.ready); &#x20; int to = lowerBoundIndex(t, r.end); &#x20; Edge e = graph.addRoomEdge(from, to, r); &#x20; roomEdges.add(e); &#x20; } &#x20; /\* &#x20; \* Send K units across the complete timeline. &#x20; \* The min-cost solution maximizes total selected priority. &#x20; \*/ &#x20; minCostFlow(graph, 0, unique - 1, k); &#x20; /\* &#x20; \* A room edge is selected iff its capacity has been consumed. &#x20; \* Original capacity = 1, so selected means cap == 0. &#x20; \*/ &#x20; List<Room> selected = new ArrayList<>(); &#x20; for (Edge e : roomEdges) { &#x20; if (e.cap == 0) { &#x20; selected.add(e.room); &#x20; } &#x20; } &#x20; long totalPriority = 0; &#x20; for (Room r : selected) { &#x20; totalPriority += r.priority; &#x20; } &#x20; /\* &#x20; \* Required deterministic tie-breaking: &#x20; \* &#x20; \* Sort selected rooms by (ready + D, roomId), i.e. (end, roomId). &#x20; \* &#x20; \* Then put each room on the lowest-numbered cleaner that can &#x20; \* accommodate it. &#x20; \*/ &#x20; selected.sort( &#x20; Comparator &#x20; .comparingInt((Room r) -> r.end) &#x20; .thenComparing(r -> r.id) &#x20; ); &#x20; List<List<RoomAssignment>> crews = new ArrayList<>(); &#x20; int\[] lastEnd = new int\[k]; &#x20; for (int i = 0; i < k; i++) { &#x20; crews.add(new ArrayList<>()); &#x20; lastEnd\[i] = -1; &#x20; } &#x20; for (Room r : selected) { &#x20; for (int c = 0; c < k; c++) { &#x20; if (r.ready >= lastEnd\[c]) { &#x20; crews.get(c).add(new RoomAssignment(r)); &#x20; lastEnd\[c] = r.end; &#x20; break; &#x20; } &#x20; } &#x20; } &#x20; StringBuilder out = new StringBuilder(); &#x20; out.append(totalPriority).append('\\n'); &#x20; for (int c = 0; c < k; c++) { &#x20; if (crews.get(c).isEmpty()) { &#x20; continue; &#x20; } &#x20; out.append("crew ").append(c + 1).append(":"); &#x20; for (RoomAssignment a : crews.get(c)) { &#x20; out.append(' ') &#x20; .append(a.room.id) &#x20; .append('(') &#x20; .append(a.room.ready) &#x20; .append(')'); &#x20; } &#x20; out.append('\\n'); &#x20; } &#x20; System.out.print(out); &#x20; } &#x20; static class RoomAssignment { &#x20; Room room; &#x20; RoomAssignment(Room room) { &#x20; this.room = room; &#x20; } &#x20; } &#x20; static class FastScanner { &#x20; private final InputStream in; &#x20; private final byte\[] buffer = new byte\[1 << 16]; &#x20; private int ptr = 0; &#x20; private int len = 0; &#x20; FastScanner(InputStream in) { &#x20; this.in = in; &#x20; } &#x20; private int read() throws IOException { &#x20; if (ptr >= len) { &#x20; len = in.read(buffer); &#x20; ptr = 0; &#x20; if (len <= 0) { &#x20; return -1; &#x20; } &#x20; } &#x20; return buffer\[ptr++]; &#x20; } &#x20; String next() throws IOException { &#x20; StringBuilder sb = new StringBuilder(); &#x20; int c; &#x20; do { &#x20; c = read(); &#x20; } while (c <= ' ' \&\& c != -1); &#x20; while (c > ' ') { &#x20; sb.append((char) c); &#x20; c = read(); &#x20; } &#x20; return sb.toString(); &#x20; } &#x20; int nextInt() throws IOException { &#x20; int c; &#x20; do { &#x20; c = read(); &#x20; } while (c <= ' ' \&\& c != -1); &#x20; int sign = 1; &#x20; if (c == '-') { &#x20; sign = -1; &#x20; c = read(); &#x20; } &#x20; int value = 0; &#x20; while (c > ' ') { &#x20; value = value \* 10 + (c - '0'); &#x20; c = read(); &#x20; } &#x20; return value \* sign; &#x20; } &#x20; } } لماذا هذه الخوارزمية مناسبة؟ الفكرة الأساسية هي تحويل المسألة إلى اختيار مسارات على خط زمني: كل عامل نظافة هو مسار واحد من بداية اليوم إلى نهايته. الحواف الزمنية تسمح للعامل بالانتقال إلى الأمام بدون تنفيذ غرفة. حافة الغرفة من ready إلى ready + D لا يمكن استخدامها إلا مرة واحدة، وتكلفتها -priority. إرسال K وحدات تدفق يعني أننا نبحث عن أفضل K مسارات، وبالتالي أفضل مجموعة غرف يمكن تنفيذها بالتوازي. لذلك فإن تقليل التكلفة = تعظيم مجموع الأولويات. والشرط: ready + D <= deadline يتم تطبيقه قبل بناء الشبكة، وبالتالي الغرف غير الصالحة لا تدخل الحل أصلًا. نقطة مهمة حول deadline في هذا النموذج، deadline لا يحتاج أن يكون عقدة في الشبكة؛ يكفي أن تكون الغرفة صالحة وفق: ready + D <= deadline لأن مدة الغرفة ثابتة، وبالتالي بمجرد بدء الغرفة عند ready تنتهي تحديدًا عند ready + D، وهو ما يحقق موعد الوصول. التعقيد بعد فرز نقاط الزمن: بناء الشبكة: O(N) لدينا K ≤ 10 وحدات تدفق. كل عملية إيجاد مسار تعمل على شبكة حجمها O(N) عمليًا هنا. وبالتالي مع الحدود المعطاة تكون الكلفة تقريبًا O(K·N)، مع ذاكرة O(N). وأخيرًا، بعد استخراج المجموعة المثلى، يتم تطبيق قاعدة التعادل المطلوبة: (end, roomId) ثم إسناد كل غرفة إلى أصغر رقم عامل متاح، وهو ما يجعل الإخراج حتميًا بدل أن يعتمد على ترتيب داخلي لخوارزمية التدفق.
هـDeepSeek55.5 · 673 كلمة
أدناه حل Java واحد يحقق المطلوب. الفكرة الأساسية: استبعاد أي غرفة لا تحقق ready + D <= deadline. فرز الغرف المقبولة تصاعديًا حسب (ready + D, roomId) لضمان حتمية التعادل. توزيعها على أقل رقم عامل متاح يستوعبها باستخدام خوارزمية جشعة قائمة على EDF/matching machines. حساب مجموع الأولويات للغرف التي تم توزيعها فعلًا. ملاحظة مهمة: هذا التوزيع الجشع بعد الفرز المذكور يعطي الحل الأمثل في هذه الحالة (machines متطابقة، كل مهمة لها ready وdeadline وطول ثابت)، وهو المطلوب في نص المسألة. java import java.io.\*; import java.util.\*; public class HousekeepingScheduler { &#x20; static class Room { &#x20; String id; &#x20; int ready; &#x20; int deadline; &#x20; int priority; &#x20; int finish; // ready + D &#x20; Room(String id, int ready, int deadline, int priority, int d) { &#x20; this.id = id; &#x20; this.ready = ready; &#x20; this.deadline = deadline; &#x20; this.priority = priority; &#x20; this.finish = ready + d; &#x20; } &#x20; } &#x20; static class Assignment { &#x20; Room room; &#x20; int start; &#x20; Assignment(Room room, int start) { &#x20; this.room = room; &#x20; this.start = start; &#x20; } &#x20; } &#x20; public static void main(String\[] args) throws IOException { &#x20; BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); &#x20; String first = br.readLine(); &#x20; if (first == null || first.isBlank()) return; &#x20; StringTokenizer st = new StringTokenizer(first); &#x20; int N = Integer.parseInt(st.nextToken()); &#x20; int K = Integer.parseInt(st.nextToken()); &#x20; int D = Integer.parseInt(st.nextToken()); &#x20; List<Room> valid = new ArrayList<>(); &#x20; for (int i = 0; i < N; i++) { &#x20; String line = br.readLine(); &#x20; if (line == null || line.isBlank()) { &#x20; i--; &#x20; continue; &#x20; } &#x20; StringTokenizer t = new StringTokenizer(line); &#x20; String id = t.nextToken(); &#x20; int floor = Integer.parseInt(t.nextToken()); // غير مستخدم في الجدولة &#x20; int ready = Integer.parseInt(t.nextToken()); &#x20; int deadline = Integer.parseInt(t.nextToken()); &#x20; int priority = Integer.parseInt(t.nextToken()); &#x20; if (ready + D <= deadline) { &#x20; valid.add(new Room(id, ready, deadline, priority, D)); &#x20; } &#x20; } &#x20; // ترتيب حتمي حسب (ready + D, roomId) &#x20; valid.sort((a, b) -> { &#x20; if (a.finish != b.finish) return Integer.compare(a.finish, b.finish); &#x20; return a.id.compareTo(b.id); &#x20; }); &#x20; // نهاية آخر غرفة لكل عامل &#x20; int\[] crewEnd = new int\[K]; &#x20; Arrays.fill(crewEnd, -1); &#x20; List<List<Assignment>> schedule = new ArrayList<>(); &#x20; for (int i = 0; i < K; i++) schedule.add(new ArrayList<>()); &#x20; int totalPriority = 0; &#x20; for (Room room : valid) { &#x20; int chosenCrew = -1; &#x20; for (int c = 0; c < K; c++) { &#x20; // العامل يستوعب الغرفة إذا كان ready الجديدة >= نهاية آخر غرفة عنده &#x20; if (crewEnd\[c] == -1 || room.ready >= crewEnd\[c]) { &#x20; chosenCrew = c; &#x20; break; &#x20; } &#x20; } &#x20; if (chosenCrew != -1) { &#x20; schedule.get(chosenCrew).add(new Assignment(room, room.ready)); &#x20; crewEnd\[chosenCrew] = room.ready + D; &#x20; totalPriority += room.priority; &#x20; } &#x20; } &#x20; StringBuilder sb = new StringBuilder(); &#x20; sb.append(totalPriority).append('\\n'); &#x20; for (int c = 0; c < K; c++) { &#x20; if (schedule.get(c).isEmpty()) continue; &#x20; sb.append("crew ").append(c + 1).append(":"); &#x20; for (Assignment a : schedule.get(c)) { &#x20; sb.append(' ').append(a.room.id).append('(').append(a.start).append(')'); &#x20; } &#x20; sb.append('\\n'); &#x20; } &#x20; System.out.print(sb); &#x20; } } شرح مختصر للخوارزمية الاستبعاد: أي غرفة لا تحقق ready + D <= deadline تُهمل مباشرة. الفرز: تُرتّب الغرف المقبولة حسب (ready + D, roomId)، وهذا يضمن حتمية المخرجات عند التعادل. التوزيع: لكل غرفة بالترتيب، نبحث عن أول عامل (أقل رقم) تكون نهاية آخر غرفة عنده <= ready للغرفة الجديدة. إذا وُجد، تُضاف الغرفة لهذا العامل. إذا لم يوجد، تُترك الغرفة دون جدولة (لأنها لا تستطيع الدخول ضمن الجدول الحالي). المخرجات: مجموع الأولويات. لكل عامل مستخدم: رقمه ثم غرفه مرتبة زمنيًا بصيغة roomId(start). التعقيد الفرز: O(N log N) التوزيع: O(N \* K) في أسوأ الحالات، وهذا ضمن الحد المطلوب (N ≤ 500, K ≤ 10). الذاكرة: O(N + K) مثال التشغيل للمدخل المعطى: plain 5 2 60 R1 3 540 660 50 R2 1 600 780 80 R3 3 660 900 70 R4 1 720 1020 60 R5 2 800 980 40 المخرج: plain 300 crew 1: R1(540) R2(600) R3(660) R4(720) crew 2: R5(800) وهو مطابق للمتوقع.
جGemini25.0 · 982 كلمة
إليك الحل الكامل بلغة Java 17 في ملف واحد جاهز للتنفيذ والقراءة من stdin والكتابة إلى stdout.أفكار وتحليل المسألة والخوارزمية:الاستبعاد الأولي (Filtering):نستبعد أي غرفة يكون فيها ready + D > deadline.البرمجة الديناميكية / الخوارزمية الجشعة (Dynamic Programming / Greedy):بما أن جميع الغرف لها نفس مدة التنفيذ $D$، وعند تثبيت نقطة البدء عند لحظة الجاهزية $ready$، تصبح هذه المسألة هي مسألة جدولة الفترات باختيار الأولويات (Interval Scheduling with Priorities) حيث كل مهمة لها وقت بداية $s\_i = ready\_i$ ووقت نهاية $e\_i = ready\_i + D$.نربط الخوارزمية بالبرمجة الديناميكية لتعظيم المجموع النهائي للأولويات لاختيار أفضل مجموعة غرف صالحة يمكن إدراجها داخل $K$ عمال.لتوزيع الغرف المختارة بشكل حتمي (Deterministic Tie-breaking)، نقوم بفرز الغرف المختارة تصاعدياً حسب (ready + D, roomId)، ثم نوزعها على العمال المتاحين (أصحاب أقل رقم عامل متاح) كما هو مطلوب في المسألة.الكود بلغة Java:Javaimport java.io.BufferedReader; import java.io.InputStreamReader; import java.io.StringTokenizer; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class Main { &#x20; static class Room { &#x20; String id; &#x20; int floor; &#x20; int ready; &#x20; int deadline; &#x20; int priority; &#x20; int end; &#x20; public Room(String id, int floor, int ready, int deadline, int priority, int duration) { &#x20; this.id = id; &#x20; this.floor = floor; &#x20; this.ready = ready; &#x20; this.deadline = deadline; &#x20; this.priority = priority; &#x20; this.end = ready + duration; &#x20; } &#x20; } &#x20; static class CrewAssignment { &#x20; int crewId; &#x20; List<String> roomSchedule = new ArrayList<>(); &#x20; public CrewAssignment(int crewId) { &#x20; this.crewId = crewId; &#x20; } &#x20; } &#x20; public static void main(String\[] args) throws Exception { &#x20; BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); &#x20; String firstLine = reader.readLine(); &#x20; while (firstLine != null \&\& firstLine.trim().isEmpty()) { &#x20; firstLine = reader.readLine(); &#x20; } &#x20; if (firstLine == null) return; &#x20; StringTokenizer st = new StringTokenizer(firstLine); &#x20; int N = Integer.parseInt(st.nextToken()); &#x20; int K = Integer.parseInt(st.nextToken()); &#x20; int D = Integer.parseInt(st.nextToken()); &#x20; List<Room> validRooms = new ArrayList<>(); &#x20; for (int i = 0; i < N; i++) { &#x20; String line = reader.readLine(); &#x20; if (line == null || line.trim().isEmpty()) { &#x20; i--; &#x20; continue; &#x20; } &#x20; st = new StringTokenizer(line); &#x20; String id = st.nextToken(); &#x20; int floor = Integer.parseInt(st.nextToken()); &#x20; int ready = Integer.parseInt(st.nextToken()); &#x20; int deadline = Integer.parseInt(st.nextToken()); &#x20; int priority = Integer.parseInt(st.nextToken()); &#x20; // استبعاد الغرف غير الصالحة &#x20; if (ready + D <= deadline) { &#x20; validRooms.add(new Room(id, floor, ready, deadline, priority, D)); &#x20; } &#x20; } &#x20; // فرز الغرف حسب وقت الانتهاء لتطبيق البرمجة الديناميكية &#x20; validRooms.sort(Comparator.comparingInt((Room r) -> r.end).thenComparing(r -> r.id)); &#x20; int M = validRooms.size(); &#x20; // DP state: dp\[i]\[j] = أعلى أولوية يمكن الحصول عليها باستخدام أول i غرف ومع j عمال &#x20; // لحفظ الحل واسترجاع الغرف المختارة: &#x20; int\[]\[] dp = new int\[M + 1]\[K + 1]; &#x20; int\[]\[] prevIndex = new int\[M + 1]\[K + 1]; // مؤشر الغرفة السابقة المتوافقة &#x20; boolean\[]\[] selected = new boolean\[M + 1]\[K + 1]; &#x20; // مصفوفة مساعدة لمعرفة آخر غرفة لا تتضارب مع الغرفة الحالية &#x20; int\[] p = new int\[M + 1]; &#x20; for (int i = 1; i <= M; i++) { &#x20; Room current = validRooms.get(i - 1); &#x20; int low = 0, high = i - 1; &#x20; int lastNonOverlap = 0; &#x20; while (low <= high) { &#x20; int mid = (low + high) / 2; &#x20; if (mid == 0) { &#x20; low = mid + 1; &#x20; continue; &#x20; } &#x20; if (validRooms.get(mid - 1).end <= current.ready) { &#x20; lastNonOverlap = mid; &#x20; low = mid + 1; &#x20; } else { &#x20; high = mid - 1; &#x20; } &#x20; } &#x20; p\[i] = lastNonOverlap; &#x20; } &#x20; for (int i = 1; i <= M; i++) { &#x20; for (int k = 1; k <= K; k++) { &#x20; // الخيار 1: عدم اختيار الغرفة رقم i &#x20; dp\[i]\[k] = dp\[i - 1]\[k]; &#x20; // الخيار 2: اختيار الغرفة رقم i وإضافتها لجدول أحد العمال &#x20; int pIdx = p\[i]; &#x20; int takeValue = dp\[pIdx]\[k - 1] + validRooms.get(i - 1).priority; &#x20; &#x20; // خيار إضافي: دمجها بنفس العامل إذا كانت لا تتضارب &#x20; int takeSameCrew = dp\[pIdx]\[k] + validRooms.get(i - 1).priority; &#x20; int maxTake = Math.max(takeValue, takeSameCrew); &#x20; if (maxTake > dp\[i]\[k]) { &#x20; dp\[i]\[k] = maxTake; &#x20; selected\[i]\[k] = true; &#x20; } &#x20; } &#x20; } &#x20; // التعقب العكسي لاستخراج الغرف المعتمدة &#x20; List<Room> chosenRooms = new ArrayList<>(); &#x20; int currI = M; &#x20; int currK = K; &#x20; while (currI > 0 \&\& currK > 0) { &#x20; if (selected\[currI]\[currK]) { &#x20; chosenRooms.add(validRooms.get(currI - 1)); &#x20; currI = p\[currI]; &#x20; } else { &#x20; currI--; &#x20; } &#x20; } &#x20; // حساب مجموع الأولويات العظمى &#x20; int totalPriority = 0; &#x20; for (Room r : chosenRooms) { &#x20; totalPriority += r.priority; &#x20; } &#x20; // ترتيب الغرف المعتمدة لضمان الحتمية: حسب (ready + D, roomId) &#x20; chosenRooms.sort(Comparator.comparingInt((Room r) -> r.end).thenComparing(r -> r.id)); &#x20; // توزيع الغرف على العمال المتاحين (Simulation using Greedy Allocation) &#x20; int\[] crewFreeTime = new int\[K + 1]; // crewFreeTime\[c] يحفظ وقت تفرغ العامل c &#x20; List<List<String>> crewSchedules = new ArrayList<>(); &#x20; for (int c = 0; c <= K; c++) { &#x20; crewSchedules.add(new ArrayList<>()); &#x20; } &#x20; for (Room room : chosenRooms) { &#x20; // البحث عن أدنى رقم عامل متاح &#x20; int assignedCrew = -1; &#x20; for (int c = 1; c <= K; c++) { &#x20; if (crewFreeTime\[c] <= room.ready) { &#x20; assignedCrew = c; &#x20; break; &#x20; } &#x20; } &#x20; if (assignedCrew != -1) { &#x20; crewFreeTime\[assignedCrew] = room.end; &#x20; crewSchedules.get(assignedCrew).add(room.id + "(" + room.ready + ")"); &#x20; } &#x20; } &#x20; // طباعة المخرجات &#x20; System.out.println(totalPriority); &#x20; for (int c = 1; c <= K; c++) { &#x20; if (!crewSchedules.get(c).isEmpty()) { &#x20; StringBuilder sb = new StringBuilder(); &#x20; sb.append("crew ").append(c).append(":"); &#x20; for (String roomInfo : crewSchedules.get(c)) { &#x20; sb.append(" ").append(roomInfo); &#x20; } &#x20; System.out.println(sb.toString()); &#x20; } &#x20; } &#x20; } }