题目描述
下课铃终于响了,你和一群朋友(共 N 人)一起冲到食堂。因为你们到的非常早,现在食堂窗口前面还没有人。食堂共有两个窗口。你们每个人打饭会耗时 a i,打完立刻去座位上吃饭会耗时 b i,由于你们吃完饭要一起打球,所以你们希望最后一个人吃完饭的时间尽可能早。现在,你要安排一种最佳的分队和排队方案使得所有人都吃完饭的时间尽量早。
输入格式
第一行一个整数 N ,表示共有 N 人。
接下来 N 行,每行两个整数a i,b i,表示每个人打饭和吃饭的用时。
输出格式
一个整数,表示所有人吃完饭的最早时间。
输入输出样例
输入
5
2 2
7 7
1 3
6 4
8 5
输出
17
思路:
这是一个经典问题(“午餐”问题),最优策略是:
- 将人按 ( b_i ) 从大到小排序。
- 用 DP:( f[i][j] ) 表示前 i 个人,第一个窗口总打饭时间为 j 时,所有前 i 个人中最晚结束时间的最小值。
转移方程
初始化
[ f[0][0] = 0 ]
转移
-
放入窗口1:
- 第一个窗口总时间 = j
- 此人结束时间 = ( j + b_i )
- 之前最晚结束时间 = ( f[i-1][j - a_i] )
- 新的最晚结束时间 = ( \max(f[i-1][j - a_i], j + b_i) )
-
放入窗口2:
- 第一个窗口总时间 j 不变
- 第二个窗口总时间 = ( \text{sumA}[i-1] - j )
- 此人结束时间 = ( (\text{sumA}[i-1] - j) + b_i )
- 新的最晚结束时间 = ( \ max(f[i-1][j], (\text{sumA}[i-1] - j) + b_i) )
算法步骤
- 按 ( b_i ) 从大到小排序
- 初始化 DP 数组
- 遍历每个人,更新 DP 状态
- 最终答案为 ( \min(f[n][j]) ) 对所有 j
代码:
#include<bits/stdc++.h>
using namespace std;
struct s{int a,b;
}p[505];
int n,c,y,f[2][250005];
bool cmp(s x,s y)
{return x.b>y.b;
}
int main(){/*freopen("meal.in","r",stdin);freopen("meal.out","w",stdout);*/scanf("%d",&n);int t=0;for (int i=1;i<=n;i++){scanf("%d%d",&p[i].a,&p[i].b);t+=p[i].a;}sort(p+1,p+n+1,cmp);memset(f,0x3f,sizeof(f));f[0][0]=0;for(int i=1;i<=n;i++){c^=1;memset(f[c],0x3f,sizeof(f[c]));y+=p[i].a;for(int j=0;j<=y;j++){//放入窗口1if (j>=p[i].a){f[c][j]=min(f[c][j],max(f[c^1][j-p[i].a],j+p[i].b));}//放入窗口2f[c][j]=min(f[c][j],max(f[c^1][j],(y-j)+p[i].b));}}int ans=INT_MAX;for(int i=0;i<=t;i++){ans=min(ans,f[c][i]);}printf("%d",ans);return 0;
}