#P1811. 第2题-小红做蛋糕
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 37
            Accepted: 13
            Difficulty: 7
            
          
          
          
                       所属公司 : 
                              阿里
                                
            
                        
              时间 :2024年4月8日-阿里云
                              
                      
          
 
- 
                        算法标签>模拟          
 
第2题-小红做蛋糕
题目思路
考虑模拟,由于时间比较大,我们无法一个一个时间枚举。可以考虑使用带有时间的事件来模拟步骤的进行。
一个做蛋糕的事件由t, i, c表示,t表示这个开始这个事件的时间,i表示做的是蛋糕的第几个步骤,c表示这个事件的蛋糕数量。我们可以将所有的事件按照时间排序,然后按照时间顺序进行模拟。
初始状态便是由n / c[0]个事件,每个事件包含c[0]个蛋糕,时间依次为t[0],  2t[0], 3t[0], ...
然后将事件放入最小堆中模拟,每次根据第一位的时间来排序。由于步骤有一定的容量限制,我们还需要维护一下每个步骤目前正在进行的步骤事件,步骤事件用tim和num分别表示目前正在占用该步骤的蛋糕的结束时间和数量。初始的时候设定所有的步骤事件的时间为0,数量就为步骤的容量。
每次事件表示它需要开始进入对应的步骤,此时查询对应步骤下可以用来做蛋糕的步骤事件,如果有个步骤事件的tim小于等于事件的启动时间t,则说明可以占用,此时就可以进入该步骤做蛋糕,并注册一个完成后需要进入下一个步骤的事件,否则如果遍历完都没有办法放完c个蛋糕,那么剩余的蛋糕就需要等待,加入事件,事件的事件暂定为所有步骤事件完成时间的最小值。
更具体的细节可以看代码。
代码
Python
import heapq
n, k = map(int, input().split(' '))
a = []
for _ in range(k):
    c, t = map(int, input().split(' '))
    a.append([c, t])
heap = []
heapq.heapify(heap)
g = [[[0, a[i][0]]] for i in range(k)]
T, cur = n // a[0][0] + (n % a[0][0] != 0), 0
for _ in range(T):
    cur += a[0][1]
    heapq.heappush(heap, [cur, 1, a[0][0]])
cur = 0
while len(heap) > 0:
    t, i, c = heapq.heappop(heap)
    if i == k:
        cur += c
        if cur >= n:
            print(t)
            break
        continue
    for cakes in g[i]:
        tim, num = cakes
        if tim <= t:
            if c <= num:
                cakes[0], cakes[1] = t + a[i][1], c
                if num - c > 0:
                    # 多余的容量
                    g[i].append([tim, num - c])
                    # 注册新事件
                heapq.heappush(heap, [t + a[i][1], i + 1, c])
                c = 0
                break
            else:
                cakes[0], cakes[1] = t + a[i][1], num
                heapq.heappush(heap, [t + a[i][1], i + 1, num])
                c -= num
    if c != 0:
        # 注册等待事件
        heapq.heappush(heap, [min(g[i])[0], i, c])
c++
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
int main() {
	int n, k;
	cin >> n >> k;
	vector<int> c(k+1, 0);
	vector<int> t(k+1, 0);
	c[0] = n;
	//t[0] = 0;
	for (int i = 1; i <= k; i++) {
		cin >> c[i] >> t[i];
	}
	vector<vector<long long>> plan; //事件结束时间,步骤,释放出的需要进行下一阶段的蛋糕数
	int cur = 0; // plan_idx
	plan.push_back({ 0, 0, n });
	for (int i = 1; i <= k; i++) {
		// 记录i阶段用工表
		queue<vector<long long>> q;
		q.push({ 0, c[i] }); //存结束时间和腾出多少空位
		while (plan[cur][1] == i - 1) { 
			int res = plan[cur][2]; // 第i步新完成的蛋糕数量
			while (res > 0) {
				long long endtime = max(plan[cur][0], q.front()[0]) + t[i];
				if (res <= q.front()[1]) {
					plan.push_back({ endtime ,i, res });
					q.push({ endtime ,res });
					q.front()[1] -= res;
					res = 0;
					if (q.front()[1] == 0) q.pop();
				}
				else {
					plan.push_back({ endtime,i, q.front()[1] });
					q.push({ endtime ,q.front()[1] });
					res -= q.front()[1];
					q.pop();
				}
			}
			cur++;
		}
	}
	long long ans = 0;
	for (const auto& p : plan) {
		ans = max(ans, p[0]);
	}
	cout << ans;
}
java
import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(); // 蛋糕数量
        int k = scanner.nextInt(); // 步骤数量
        int[][] task = new int[k+1][2]; // 步骤和时间
        for(int i = 1; i <= k; i++) {
            task[i][0] = scanner.nextInt(); // 人数
            task[i][1] = scanner.nextInt(); // 时间
        }
        solve(n, k, task);
    }
    public static void solve(int n, int k, int[][] task) {
        long[][] people = new long[k+1][1005]; // 每个步骤的人的结束时间
        long[] cakeTime = new long[n+1]; // 蛋糕完成时间
        for(int i = 1; i <= k; i++) { // 枚举每个步骤
            for(int j = 1; j <= n; j++) { // 枚举每个蛋糕
                int person = (j-1) % task[i][0] + 1; // 当前操作的人
                cakeTime[j] = Math.max(cakeTime[j], people[i][person]) + task[i][1]; //更新蛋糕的时间
                people[i][person] = cakeTime[j]; // 更新操作人的结束时间
            }
        }
        Arrays.sort(cakeTime); // 结束后排序
        System.out.println(cakeTime[n]); // 输出最后一个蛋糕的完成时间,即完成所有蛋糕的最短时间
    }
}
会员可通过查看《已通过》的提交记录来查看其他语言哦~
题目描述
小红很喜欢做蛋糕,做蛋糕有k个步骤,第i个步骤有ci个人可以完成,每个人只能做同时做一个蛋糕,并且只会那一个步骤,也就是最多可 以同时有ci个蛋糕在进行第i个步骤。一个蛋糕的第i个步骤需要ti的时间。小红想要做n个蛋糕,问最少需要多少时间可以完成。
输入描述
第一行两个整数 n,k,表示蛋糕数量和步骤数量。
接下来k行,每行两个整数ci,ti,表示第i个步骤的人数和时间。
1<=n,k,ci<=1000
1<=t<=109
输出描述
输出一个整数,表示最少需要的时间.
样例
输入
3 3
2 1
2 2
3 1
输出
6
说明
第一秒,两个蛋糕完成步骤一。
第二秒,一个蛋糕完成步骤一,两个蛋糕完成步骤二的第一秒。
第三秒,两个蛋糕完成步骤二。一个蛋糕等待步骤二。
第四秒,两个蛋糕完成步骤三。一个蛋糕完成步骤二的第一秒。
第五秒,一个蛋糕完成步骤二。
第六秒,一个蛋糕完成步骤三。