题目描述
上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂。这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭。由于每个人的口味(以及胃口)不同,所以他们要吃的菜各有不同,打饭所要花费的时间是因人而异的。另外每个人吃饭的速度也不尽相同,所以吃饭花费的时间也是可能有所不同的。
THU ACM小组的吃饭计划是这样的:先把所有的人分成两队,并安排好每队中各人的排列顺序,然后一号队伍到一号窗口去排队打饭,二号队伍到二号窗口去排队打饭。每个人打完饭后立刻开始吃,所有人都吃完饭后立刻集合去六教地下室进行下午的训练。
现在给定了每个人的打饭时间和吃饭时间,要求安排一种最佳的分队和排队方案使得所有人都吃完饭的时间尽量早。
假设THU ACM小组在时刻0到达十食堂,而且食堂里面没有其他吃饭的同学(只有打饭的师傅)。每个人必须而且只能被分在一个队伍里。两个窗口是并行操作互不影响的,而且每个人打饭的时间是和窗口无关的,打完饭之后立刻就开始吃饭,中间没有延迟。
现在给定N个人各自的打饭时间和吃饭时间,要求输出最佳方案下所有人吃完饭的时刻。
输入格式
第一行一个整数N,代表总共有N个人。
以下N行,每行两个整数 Ai,Bi。依次代表第i个人的打饭时间和吃饭时间。
输出格式
一个整数T,代表所有人吃完饭的最早时刻。
所有输入数据均为不超过200的正整数。
容易想到的一个贪心策略就是:让打饭慢的人先打饭。所以我们一开始可以对所有人按照打饭时间排序。
然后我们开始考虑窗口的选择。显然,前i个人的最优解一定是由前i-1个人的最优解更新而来的,所以我们可以考虑用动态规划。考虑第i个人怎么选窗口会最优。
第i个人选窗口会影响到的因素就是:在1窗口打饭一共用了多少时间、在2窗口打饭一共用了多少时间。那么我们把这两个量设计进状态即可。设dp[i][j][k]表示前i个人在1窗口用了j个单位时间、在2窗口用了k个单位时间的情况下,前i个人吃完饭的最早时刻。
那么转移很好想,O(1)转移即可。但是进一步分析会发现一个问题:j和k的上限为200∗200=40000,这样开数组肯定会爆的...那么想一想优化。
可以发现j和k加起来刚好等于前i个人的打饭时间和。那么换个思路,我们求出前i个人的打饭时间和sum[i],那么我们只需要知道j就可以求出k了。那么我们的状态就可以省掉一维:dp[i][j]表示前i个人在1号窗口用了j个单位时间的情况下,前i个人吃完饭的最早时刻。可以得出转移方程:
dp[i][j]=⎧⎪⎨⎪⎩min(max(dp[i−1][j−p[i].a],j+p[i].b),max(dp[i−1][j],k+p[i].b))j,k>=a[i]max(dp[i−1][j−p[i].a],j+p[i].b)j>=a[i]max(dp[i−1][j],k+p[i].b)k>=a[i]
初始化dp[i][j]=inf,dp[0][0]=0。答案就是Max{dp[n][i]}
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 201
#define maxm 40001
#define inf 0x3f3f3f3f
using namespace std;
inline int read(){
register int x(0),f(1); register char c(getchar());
while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
struct node{ int a,b; }p[maxn];
int dp[maxn][maxm];
int n,c[maxn];
inline bool cmp(const node &x,const node &y){ return x.b>y.b; }
int main(){
n=read();
for(register int i=1;i<=n;i++) p[i].a=read(),p[i].b=read();
sort(p+1,p+1+n,cmp);
for(register int i=1;i<=n;i++) c[i]=c[i-1]+p[i].a;
memset(dp,0x3f,sizeof dp),dp[0][0]=0;
for(register int i=1;i<=n;i++){
for(register int j=0;j<=c[i];j++){
register int k=c[i]-j;
if(j>=p[i].a && k>=p[i].a) dp[i][j]=min( max( dp[i-1][j-p[i].a],j+p[i].b ),max( dp[i-1][j],k+p[i].b ) );
else if(j>=p[i].a) dp[i][j]=max( dp[i-1][j-p[i].a],j+p[i].b );
else if(k>=p[i].a) dp[i][j]=max( dp[i-1][j],k+p[i].b );
}
}
int ans=inf;
for(register int i=0;i<=c[n];i++) ans=min( ans,dp[n][i] );
printf("%d\n",ans);
return 0;
}
时间复杂度为O(N∗40000)
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· C# 13 中的新增功能实操
· Ollama本地部署大模型总结
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(4)
· langchain0.3教程:从0到1打造一个智能聊天机器人
· 2025成都.NET开发者Connect圆满结束