n 个人合伙开店,i 号的股份是 w_i。 把所有股份加起来的值记作 W。 店里每个月把当月的盈亏 A 元按股份的比例分配。 A 为负数表示这个月亏损,这时每个人的份额都不超过 0,意思是要拿出这么多钱。 第一行是 n 和月数 q,第二行是 w_1 到 w_n,接下来 q 行每行给出一个月的 A。 i 号的份额先定为不超过 A × w_i ÷ W 的最大整数。 例如 3.5 得 3,−3.5 得 −4,能整除时就是那个值本身。 这时 i 号被舍去的部分是 A × w_i − 份额 × W,这个值是 0 以上、小于 W 的整数。 这样定出的份额加起来比 A 少 r 元(0 ≤ r < n)。 剩下的 r 元,从被舍去的部分最大的人开始,给 r 个人每人再加 1 元。 被舍去的部分相同时,编号小的人先得。 对每个月,把 1 号到 n 号最终的份额用一个空格隔开输出在一行。 输出的份额加起来总是等于 A。
输入
1 ≤ n ≤ 1,000,1 ≤ q ≤ 20,1 ≤ w_i ≤ 1,000,000,−1,000,000,000 ≤ A ≤ 1,000,000,000。 都是整数,负数前面带 -
import sysdef split(amount, weights, total): """amount를 weights 비율로 나눈 몫 목록. 내림으로 나누고 남은 돈은 버린 부분이 큰 사람부터 1씩 준다.""" shares = [amount * w // total for w in weights] # //는 음수에서도 내림(-7 // 2 == -4) cut = [amount * w - s * total for w, s in zip(weights, shares)] # 0 이상 total 미만 left = amount - sum(shares) order = sorted(range(len(weights)), key=lambda i: (-cut[i], i)) for i in order[:left]: shares[i] += 1 return sharesdef main(): data = sys.stdin.read().split() n, q = int(data[0]), int(data[1]) weights = [int(x) for x in data[2:2 + n]] total = sum(weights) out = [] for t in range(q): amount = int(data[2 + n + t]) out.append(" ".join(map(str, split(amount, weights, total)))) print("\n".join(out))main()
JavaScriptmain.js · 21 行
const data = require("fs").readFileSync(0, "utf8").split(/\s+/).filter((x) => x !== "").map(Number);// amount를 weights 비율로 나눈 몫 배열. 내림으로 나누고 남은 돈은 버린 부분이 큰 사람부터 1씩 준다.function split(amount, weights, total) { // Math.floor는 음수에서도 내림이다(Math.trunc는 0 쪽으로 버림). 곱은 10^15 이하라 정확하다 const shares = weights.map((w) => Math.floor((amount * w) / total)); const cut = weights.map((w, i) => amount * w - shares[i] * total); // 0 이상 total 미만 let left = amount; for (const s of shares) left -= s; const order = weights.map((_, i) => i).sort((a, b) => cut[b] - cut[a] || a - b); for (let k = 0; k < left; k++) shares[order[k]] += 1; return shares;}const n = data[0];const q = data[1];const weights = data.slice(2, 2 + n);const total = weights.reduce((a, b) => a + b, 0);const out = [];for (let t = 0; t < q; t++) out.push(split(data[2 + n + t], weights, total).join(" "));console.log(out.join("\n"));
TypeScriptmain.ts · 23 行
import { readFileSync } from "node:fs";const data: number[] = readFileSync(0, "utf8").split(/\s+/).filter((x: string) => x !== "").map(Number);// amount를 weights 비율로 나눈 몫 배열. 내림으로 나누고 남은 돈은 버린 부분이 큰 사람부터 1씩 준다.function split(amount: number, weights: number[], total: number): number[] { // Math.floor는 음수에서도 내림이다(Math.trunc는 0 쪽으로 버림). 곱은 10^15 이하라 정확하다 const shares: number[] = weights.map((w: number) => Math.floor((amount * w) / total)); const cut: number[] = weights.map((w: number, i: number) => amount * w - shares[i] * total); // 0 이상 total 미만 let left = amount; for (const s of shares) left -= s; const order: number[] = weights.map((_: number, i: number) => i).sort((a: number, b: number) => cut[b] - cut[a] || a - b); for (let k = 0; k < left; k++) shares[order[k]] += 1; return shares;}const n: number = data[0];const q: number = data[1];const weights: number[] = data.slice(2, 2 + n);const total: number = weights.reduce((a: number, b: number) => a + b, 0);const out: string[] = [];for (let t = 0; t < q; t++) out.push(split(data[2 + n + t], weights, total).join(" "));console.log(out.join("\n"));
JavaMain.java · 46 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.Arrays;import java.util.StringTokenizer;public class Main { // amount를 weights 비율로 나눈 몫 배열. 내림으로 나누고 남은 돈은 버린 부분이 큰 사람부터 1씩 준다. static long[] split(long amount, int[] weights, long total) { int n = weights.length; long[] shares = new long[n]; long[] cut = new long[n]; long left = amount; for (int i = 0; i < n; i++) { shares[i] = Math.floorDiv(amount * weights[i], total); // /는 0 쪽으로 버리고 floorDiv는 내림 cut[i] = Math.floorMod(amount * weights[i], total); // %는 음수가 나올 수 있고 floorMod는 0 이상 left -= shares[i]; } Integer[] order = new Integer[n]; for (int i = 0; i < n; i++) order[i] = i; Arrays.sort(order, (a, b) -> cut[a] != cut[b] ? Long.compare(cut[b], cut[a]) : Integer.compare(a, b)); for (int k = 0; k < left; k++) shares[order[k]]++; return shares; } public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(in.readLine()); int n = Integer.parseInt(st.nextToken()), q = Integer.parseInt(st.nextToken()); int[] weights = new int[n]; long total = 0; st = new StringTokenizer(in.readLine()); for (int i = 0; i < n; i++) { weights[i] = Integer.parseInt(st.nextToken()); total += weights[i]; } StringBuilder out = new StringBuilder(); for (int t = 0; t < q; t++) { long amount = Long.parseLong(in.readLine().trim()); long[] shares = split(amount, weights, total); for (int i = 0; i < n; i++) out.append(i > 0 ? " " : "").append(shares[i]); out.append('\n'); } System.out.print(out); }}
C#Main.cs · 48 行
using System;using System.Linq;using System.Text;class Program{ // C#의 /는 0 쪽으로 버리므로, 나머지가 음수이면 1을 빼서 내림으로 만든다 static long FloorDiv(long a, long b) { long quot = a / b; if (a % b < 0) quot--; return quot; } // amount를 weights 비율로 나눈 몫 배열. 내림으로 나누고 남은 돈은 버린 부분이 큰 사람부터 1씩 준다. static long[] Split(long amount, int[] weights, long total) { int n = weights.Length; var shares = new long[n]; var cut = new long[n]; long left = amount; for (int i = 0; i < n; i++) { shares[i] = FloorDiv(amount * weights[i], total); cut[i] = amount * weights[i] - shares[i] * total; // 0 이상 total 미만 left -= shares[i]; } // OrderByDescending은 안정 정렬이라 버린 부분이 같으면 번호가 작은 사람이 앞에 남는다 int[] order = Enumerable.Range(0, n).OrderByDescending(i => cut[i]).ToArray(); for (int k = 0; k < left; k++) shares[order[k]]++; return shares; } static void Main() { string[] head = Console.ReadLine().Trim().Split(' '); int n = int.Parse(head[0]), q = int.Parse(head[1]); int[] weights = Console.ReadLine().Trim().Split(' ').Select(int.Parse).ToArray(); long total = weights.Sum(w => (long)w); var sb = new StringBuilder(); for (int t = 0; t < q; t++) { long amount = long.Parse(Console.ReadLine().Trim()); sb.Append(string.Join(" ", Split(amount, weights, total))).Append('\n'); } Console.Write(sb); }}
C++main.cpp · 49 行
#include <algorithm>#include <iostream>#include <numeric>#include <vector>using namespace std;// C++의 /는 0 쪽으로 버리므로, 나머지가 음수이면 1을 빼서 내림으로 만든다long long floorDiv(long long a, long long b) { long long quot = a / b; if (a % b < 0) quot--; return quot;}// amount를 weights 비율로 나눈 몫 목록. 내림으로 나누고 남은 돈은 버린 부분이 큰 사람부터 1씩 준다.vector<long long> split(long long amount, const vector<int>& weights, long long total) { int n = (int)weights.size(); vector<long long> shares(n), cut(n); long long left = amount; for (int i = 0; i < n; i++) { shares[i] = floorDiv(amount * weights[i], total); cut[i] = amount * weights[i] - shares[i] * total; // 0 이상 total 미만 left -= shares[i]; } vector<int> order(n); iota(order.begin(), order.end(), 0); // stable_sort라서 버린 부분이 같으면 번호가 작은 사람이 앞에 남는다 stable_sort(order.begin(), order.end(), [&](int a, int b) { return cut[a] > cut[b]; }); for (int k = 0; k < left; k++) shares[order[k]]++; return shares;}int main() { int n, q; cin >> n >> q; vector<int> weights(n); long long total = 0; for (int& w : weights) { cin >> w; total += w; } for (int t = 0; t < q; t++) { long long amount; cin >> amount; vector<long long> shares = split(amount, weights, total); for (int i = 0; i < n; i++) cout << (i ? " " : "") << shares[i]; cout << "\n"; } return 0;}
Cmain.c · 48 行
#include <stdio.h>#include <stdlib.h>#define MAXN 1000static long long cut[MAXN];/* C의 /는 0 쪽으로 버리므로, 나머지가 음수이면 1을 빼서 내림으로 만든다 */static long long floor_div(long long a, long long b) { long long quot = a / b; if (a % b < 0) quot--; return quot;}/* 버린 부분이 큰 사람이 앞, 같으면 번호가 작은 사람이 앞(qsort는 안정 정렬이 보장되지 않아 번호까지 비교한다) */static int by_cut(const void *x, const void *y) { int a = *(const int *)x, b = *(const int *)y; if (cut[a] != cut[b]) return cut[a] > cut[b] ? -1 : 1; return a - b;}int main(void) { static int weights[MAXN], order[MAXN]; static long long shares[MAXN]; int n, q; if (scanf("%d %d", &n, &q) != 2) return 0; long long total = 0; for (int i = 0; i < n; i++) { if (scanf("%d", &weights[i]) != 1) return 0; total += weights[i]; } for (int t = 0; t < q; t++) { long long amount; if (scanf("%lld", &amount) != 1) return 0; long long left = amount; for (int i = 0; i < n; i++) { shares[i] = floor_div(amount * weights[i], total); cut[i] = amount * weights[i] - shares[i] * total; /* 0 이상 total 미만 */ left -= shares[i]; order[i] = i; } qsort(order, (size_t)n, sizeof order[0], by_cut); for (int k = 0; k < left; k++) shares[order[k]]++; for (int i = 0; i < n; i++) printf(i ? " %lld" : "%lld", shares[i]); printf("\n"); } return 0;}
Craykonmain.cray · 39 行
head = 控制台.读数字();n = head[0];q = head[1];weights = 控制台.读数字();total = 0;遍历 (w 在 weights){ total = total + w;}cut = 新列表(n, 0);// 버린 부분이 큰 사람이 앞. 정렬은 안정 정렬이라 같으면 번호가 작은 사람이 앞에 남는다byCut(a, b){ 返回 cut[b] - cut[a];}循环 (t = 0; t < q; t++){ amount = 控制台.读行().去空白().转数字(); shares = []; order = []; left = amount; 循环 (i = 0; i < n; i++) { // 수학.내림은 음수에서도 내림이다(수학.몫은 0 쪽으로 버림). 곱은 10^15 이하라 정확하다 share = 数学.向下取整(amount * weights[i] / total); cut[i] = amount * weights[i] - share * total; // 0 이상 total 미만 left = left - share; shares.添加(share); order.添加(i); } order.排序(byCut); 循环 (k = 0; k < left; k++) { shares[order[k]] = shares[order[k]] + 1; } 控制台.输出(shares.连接(" "));}