#P3331. 第1题-银行
-
1000ms
Tried: 241
Accepted: 38
Difficulty: 6
所属公司 :
京东
时间 :2025年8月2日
-
算法标签>贪心算法
第1题-银行
题面描述
- 问题:有n个人,第i个人的基础办理时长为a[i]。由于业务特殊性,若他在开始办理前等待了W分钟,则其实际办理时长变为a[i]+b[i]∗W。银行只有一个窗口,同一时刻只能服务一人。请安排办理顺序,使所有人的实际办理时长之和最小,并输出最小值对 1000000009 取模。
- 输入:第一行n(1≤n≤10000),接下来n行每行a[i],b[i](1≤a[i],b[i]≤10000)。
- 输出:一个整数,为最小总时长对1000000009取模。
思路
-
关键化简:总的实际办理时长之和等于整个队伍的结束时间(最后一人的结束时刻),记为 Dn。若当前累积时间为C,处理第i个人之后变为
C′=ai+(1+bi)⋅C即每个人对应一个仿射函数gi(x)=ai+(1+bi)⋅x。总时间即为gpi(n)⋯gpi(1)(0)。
-
交换论证:考虑相邻两人i 与j,在某个前缀累积时间为X 下:
gj(gi(X))−gi(gj(X))=aibj−ajbi与 X 无关。因此,当且仅当 aibj>ajbi 时,交换为j,i更优。
-
排序规则:将人员按比值ai/bi 升序排序(用交叉乘避免除法),即可得到最优顺序。
-
递推求值:按该顺序从前到后递推

最终cur 即为答案。
C++
#include <bits/stdc++.h>
using namespace std;
static const long long MOD = 1000000009LL;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
if (!(cin >> n)) return 0;
struct Node { long long a, b; };
vector<Node> v(n);
for (int i = 0; i < n; ++i) {
cin >> v[i].a >> v[i].b;
}
// 按 a/b 升序排序(用交叉乘比较,避免浮点)
stable_sort(v.begin(), v.end(), [](const Node& x, const Node& y){
// x.a/x.b < y.a/y.b <=> x.a * y.b < y.a * x.b
long long lhs = x.a * y.b;
long long rhs = y.a * x.b;
if (lhs != rhs) return lhs < rhs;
// 比值相同任意,做个稳定细分(非必要)
return x.b < y.b;
});
// 递推 cur = a + (1+b)*cur
long long cur = 0;
for (auto &it : v) {
long long term = ( ( (1 + it.b) % MOD ) * cur ) % MOD;
cur = (term + (it.a % MOD)) % MOD;
}
cout << (cur % MOD + MOD) % MOD << "\n";
return 0;
}
Python
import sys
MOD = 1000000009
def main():
data = sys.stdin.read().strip().split()
if not data:
return
it = iter(data)
n = int(next(it))
arr = []
for _ in range(n):
a = int(next(it)); b = int(next(it))
arr.append((a, b))
# 按 a/b 升序,用交叉乘法比较
arr.sort(key=lambda x: (x[0] / x[1], x[1])) # 简便写法,但有浮点
# 为避免浮点,可改为自定义比较器;此处用装饰器法实现稳定性
# 如果担心浮点,可使用下面注释的排序方式(需要 Python3.10+ 可用 functools.cmp_to_key):
# from functools import cmp_to_key
# def cmp(p, q):
# a1, b1 = p; a2, b2 = q
# val = a1 * b2 - a2 * b1
# if val < 0: return -1
# if val > 0: return 1
# return (b1 > b2) - (b1 < b2)
# arr.sort(key=cmp_to_key(cmp))
cur = 0
for a, b in arr:
cur = ( ( (1 + b) % MOD ) * cur + a ) % MOD
print(cur % MOD)
if __name__ == "__main__":
main()
Java
import java.io.*;
import java.util.*;
public class Main {
static final long MOD = 1000000009L;
static class FastScanner {
BufferedInputStream in = new BufferedInputStream(System.in);
byte[] buffer = new byte[1 << 16];
int ptr = 0, len = 0;
int read() throws IOException {
if (ptr >= len) {
len = in.read(buffer);
ptr = 0;
if (len <= 0) return -1;
}
return buffer[ptr++];
}
String next() throws IOException {
StringBuilder sb = new StringBuilder();
int c;
while ((c = read()) != -1 && c <= ' ') {}
if (c == -1) return null;
do {
sb.append((char)c);
c = read();
} while (c != -1 && c > ' ');
return sb.toString();
}
}
static class Node {
long a, b;
Node(long a, long b) { this.a = a; this.b = b; }
}
public static void main(String[] args) throws Exception {
FastScanner fs = new FastScanner();
String s = fs.next();
if (s == null) return;
int n = Integer.parseInt(s);
List<Node> list = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
long a = Long.parseLong(fs.next());
long b = Long.parseLong(fs.next());
list.add(new Node(a, b));
}
// 按 a/b 升序排序(比较 a1*b2 与 a2*b1)
list.sort((p, q) -> {
long lhs = p.a * q.b;
long rhs = q.a * p.b;
if (lhs < rhs) return -1;
if (lhs > rhs) return 1;
// 比值相同任意,细分一下(非必要)
return Long.compare(p.b, q.b);
});
// 递推 cur = a + (1+b)*cur (取模)
long cur = 0;
for (Node nd : list) {
long term = ((1 + nd.b) % MOD) * (cur % MOD) % MOD;
cur = (term + (nd.a % MOD)) % MOD;
}
System.out.println((cur % MOD + MOD) % MOD);
}
}
题目内容
某个银行只有一个营业员,只能同时办理一个人的业务。因为营业员人数有限,所以除了首个办理业务的人之外,其他所有人都需要等待前一个办理业务的人办理完成。
假设现在有n个人需要办理业务,第i个人的业务需要办理a[i]分钟
由于业务的特殊性,第i个人每等1分钟,他最终办理业务的时间就会多b[i]分钟.
作为志愿者,你的任务就是安排他们的先后顺序,使每个人办理业务的总时间(包括等待时间)之和最小。
输入描述
第一行一个数n,表示有n个人(1<=n<=10000)。
接下来n行,每行两个数a[i],b[i](1<=a[i],b[i]<=10000)。
输出描述
一行一个整数,输出最小时间。如果答案过大,需要对1000000009取模(求余)
样例1
输入
2
1 1
2 5
输出
5
说明
若第1个人先办理业务,然后第2个人办理业务:
第1个人办理业务的时间是1分钟
第2个人办理业务的时间是2+1∗5=7分钟
总时间之和为8分钟
若第2个人先办理业务,然后第1个人办理业务:
第2个人办理业务的时间是2分钟
第1个人办理业务的时间是1+2∗1=3分钟
总时间之和为5分钟
所以最小时间是5分钟。