#P3187. 贪心歌手(200分)
-
1000ms
Tried: 192
Accepted: 60
Difficulty: 5
所属公司 :
华为od
-
算法标签>贪心算法
贪心歌手(200分)
题面描述
一个歌手需要从城市 A 前往城市 B 参加演出,必须在 T 天内到达。途中会经过 N 座城市,歌手不能往回走。每两座城市之间的行程天数已知。歌手在每座城市可以卖唱赚钱,卖唱的收入每天递减。歌手到达某座城市后的第二天才能开始卖唱,且如果今天卖唱,第二天才能出发。目标是计算歌手在 T 天内最多能赚多少钱。
详细思路
1. 问题理解
- 目标:歌手需要在 T 天内从 A 城赶到 B 城,途中经过 N 座城市。每两座城市之间的行程天数已知,且歌手不能往回走。
- 卖唱规则:
- 歌手到达某座城市后的第二天才能开始卖唱。
- 如果今天卖唱,第二天才能出发。
- 卖唱的收入每天递减:第一天赚 M,第二天赚 M−D,第三天赚 M−2D,直到收入减少到 0 为止。
- 问题:如何在 T 天内分配卖唱天数,使得总收入最大化?
2. 问题分解
-
计算总行程天数:
- 从 A 到 B 的总行程天数是所有城市之间行程天数的总和。
- 设总行程天数为 totalTravelDays。
-
计算剩余天数:
- 剩余天数 remainingDays=T−totalTravelDays。
- 这些剩余天数可以分配给途经的 N 座城市,用于卖唱。
-
卖唱收入计算:
- 对于每座城市,卖唱 k 天的总收入为: 收入=M+(M−D)+(M−2D)+⋯+(M−(k−1)D) 这是一个等差数列,其和为: 收入=k⋅M−D⋅2(k−1)⋅k
- 需要注意的是,收入不能为负数,因此当 M−(k−1)D≤0 时,停止计算。
-
分配剩余天数:
- 目标是最大化总收入,因此需要将剩余天数分配给收入最高的天数。
- 由于收入是递减的,优先选择收入高的天数进行分配。
3. 贪心策略
-
收入递减性质:
- 每座城市的卖唱收入是递减的,因此卖唱的天数越多,边际收益越低。
- 为了最大化总收入,应该优先选择当前收入最高的天数。
-
优先队列(堆)的使用:
- 使用一个最大堆(优先队列)来存储所有可能的卖唱收入。
- 对于每座城市,计算卖唱 k 天的收入,并将这些收入存入堆中。
- 每次从堆中取出最大的收入,累加到总收入中,直到剩余天数用完。
-
算法步骤:
- 遍历每座城市,计算卖唱 k 天的收入,存入堆。
- 从堆中取出最大的 remainingDays 个收入,累加得到总收入。
cpp
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
int main() {
int T, N;
cin >> T >> N;
vector<int> travelDays(N + 1);
for (int i = 0; i <= N; ++i) {
cin >> travelDays[i];
}
vector<pair<int, int>> cities(N);
for (int i = 0; i < N; ++i) {
cin >> cities[i].first >> cities[i].second;
}
// 计算总行程天数
int totalTravelDays = 0;
for (int day : travelDays) {
totalTravelDays += day;
}
// 剩余的天数可以用于卖唱
int remainingDays = T - totalTravelDays;
// 使用优先队列(最大堆)来存储每座城市的收入
priority_queue<int> maxHeap;
// 计算每座城市卖唱 k 天的总收入
for (auto& city : cities) {
int M = city.first;
int D = city.second;
int k = 0;
while (M - k * D > 0 && k < remainingDays) {
maxHeap.push(M - k * D);
k++;
}
}
// 从堆中取出最大的 remainingDays 个收入
int totalIncome = 0;
while (!maxHeap.empty() && remainingDays > 0) {
totalIncome += maxHeap.top();
maxHeap.pop();
remainingDays--;
}
cout << totalIncome << endl;
return 0;
}
python
import heapq
def main():
T, N = map(int, input().split())
travel_days = list(map(int, input().split()))
cities = [tuple(map(int, input().split())) for _ in range(N)]
total_travel_days = sum(travel_days)
remaining_days = T - total_travel_days
max_heap = []
for M, D in cities:
k = 0
while M - k * D > 0 and k < remaining_days:
heapq.heappush(max_heap, -(M - k * D)) # 使用负数模拟最大堆
k += 1
total_income = 0
while max_heap and remaining_days > 0:
total_income += -heapq.heappop(max_heap)
remaining_days -= 1
print(total_income)
if __name__ == "__main__":
main()
java
import java.util.*;
public class Main {
public static void main(String[] args) {
// 创建 Scanner 对象用于读取输入
Scanner sc = new Scanner(System.in);
// 读取总天数 T 和城市数量 N
int T = sc.nextInt();
int N = sc.nextInt();
// 读取每两座城市之间的行程天数
int[] travelDays = new int[N + 1];
for (int i = 0; i <= N; i++) {
travelDays[i] = sc.nextInt();
}
// 读取每座城市的卖唱收入 M 和递减值 D
int[][] cities = new int[N][2];
for (int i = 0; i < N; i++) {
cities[i][0] = sc.nextInt(); // M
cities[i][1] = sc.nextInt(); // D
}
// 计算总行程天数
int totalTravelDays = 0;
for (int day : travelDays) {
totalTravelDays += day;
}
// 计算剩余可以用于卖唱的天数
int remainingDays = T - totalTravelDays;
// 创建一个最大堆(优先队列),用于存储每座城市的卖唱收入
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
// 遍历每座城市,计算卖唱 k 天的收入,并存入最大堆
for (int[] city : cities) {
int M = city[0]; // 第一天的收入
int D = city[1]; // 每天收入的递减值
int k = 0; // 卖唱的天数
// 计算卖唱 k 天的收入,直到收入为 0 或剩余天数用完
while (M - k * D > 0 && k < remainingDays) {
maxHeap.offer(M - k * D); // 将收入加入最大堆
k++;
}
}
// 计算总收入
int totalIncome = 0;
while (!maxHeap.isEmpty() && remainingDays > 0) {
totalIncome += maxHeap.poll(); // 取出当前最大收入并累加
remainingDays--; // 剩余天数减 1
}
// 输出最大总收入
System.out.println(totalIncome);
}
}
题目内容
一个歌手准备从 A 城去 B 城参加演出。
1.按照合同,他必须在 T 天内赶到
2.歌手途经 N 座城市
3.歌手不能往回走
4.每两座城市之间需要的天数都可以提前获知。
5.歌手在每座城市都可以在路边卖唱赚钱。
经过调研,歌手提前获知了每座城市卖唱的收入预期: 如果在一座城市第一天卖唱可以赚 M,后续每天的收入会减少 D(第二天赚的钱是 M−D,第三天是 M−2D ...)。如果收入减少到 0 就不会再少了。
6.歌手到达后的第二天才能开始卖唱。如果今天卖过唱,第二天才能出发。
贪心的歌手最多可以赚多少钱?
输入描述
第一行两个数字 T 和 N,中间用空格隔开。
-
T 代表总天数,0<T<1000
-
N 代表路上经过 N 座城市,0<N<100
第二行 N+1 个数字,中间用空格隔开。代表每两座城市之间耗费的时间。
- 其总和 ≤T。
接下来 N 行,每行两个数字 M 和 D,中间用空格隔开。代表每个城市的输入预期。
-
0<M<1000
-
0<D<100
输出描述
一个数字。代表歌手最多可以赚多少钱。以回车结束。
样例1
输入
10 2
1 1 2
120 20
90 10
输出
540
说明
总共10天,路上经过2座城市。
路上共花 1+1+2=4 天
剩余6天最好的计划是在第一座城市待3天,在第二座城市待3天。
在第一座城市赚的钱:120+100+80=300
在第二座城市赚的钱:90+80+70=240
一共 300+240=540。