题目简述
THU ACM小组一行N个人去食堂吃饭,计划是这样的:先把所有的人分成两队,并安排好每队中各人的排列顺序,然后一号队伍到一号窗口去排队打饭,二号队伍到二号窗口去排队打饭。每个人打完饭后立刻开始吃,所有人都吃完饭后立刻集合去六教地下室进行下午的训练。
现在给定了每个人的打饭时间和吃饭时间,要求安排一种最佳的分队和排队方案使得所有人都吃完饭的时间尽量早。
假设THU ACM小组在时刻0到达十食堂,而且食堂里面没有其他吃饭的同学(只有打饭的师傅)。每个人必须而且只能被分在一个队伍里。两个窗口是并行操作互不影响的,而且每个人打饭的时间是和窗口无关的,打完饭之后立刻就开始吃饭,中间没有延迟。
现在给定N个人各自的打饭时间和吃饭时间,要求输出最佳方案下所有人吃完饭的时刻。
输入输出格式
输入格式:
第一行一个整数N,代表总共有N个人。
以下N行,每行两个整数 Ai,Bi。依次代表第i个人的打饭时间和吃饭时间。
输出格式:
一个整数T,代表所有人吃完饭的最早时刻。
说明
所有输入数据均为不超过200的正整数。
solution
一道比较难想的动态规划(只是对于本蒟蒻),这道题的大体思路是贪心+DP
贪心是将N个人按照Bi的顺序排序,吃饭时间长的人放到前面盛饭
首先简化问题,假定只有一个打饭窗口,且定义延后时间T,第i个人的延后时间记作T[i],则T[i]=max(eat[i] - Σ(i<j<=N)time[j] , 0)其中j∈Z,N为总人数,time[j]表示第j个人的打饭时间,eat[j],表示第j个人的吃饭时间。显然,最后的集合时间为Σ(1<=j<=N)time[j] + max{T[j] , j∈[1,N]}。
将人按照eat大小从大到小排序后,可以证明此时max{T[j] , j∈[1,N]}最小,因为任何顺序改变都会导致max{T[j] , j∈[1,N]}增加(或至少持平),而Σ(1<=j<=N)time[j]不会改变。所以将人按照eat大小从大到小排序后,最后集合时间最短。我们可以很容易地将此结论推广到两支队伍的情况,从而证明贪心的正确性。
然后进行DP
DP[i][j]表示i个人盛饭排队共耗时j时最少话费的总时间
得出状态转移方程
for(re int i=1;i<=n;++i)//i表示当前第i个人 { for(re int j=0;j<=sum[i];++j)//j表示当前花费的等待时间 { if(j>=stu[i].a) DP[i][j]=min(DP[i][j],max(DP[i-1][j-stu[i].a],j+stu[i].b));//第一个队列花费的时间由max(DP[i-1][j-stu[i].a],j+stu[i].b)转移而来
//DP[i-1][j-stu[i].a]表示除去第i个人的总时间,因为有可能前面i-1个人还没吃饭第i个人就吃完了(队伍最后离开的是前i-1个人)
//j+stu[i]表示第i个人排队吃饭的时间(队伍最后离开的是第i个人)
DP[i][j]=min(DP[i][j],max(DP[i-1][j],sum[i]-j+stu[i].b));//i在第二个队列的情况
//同上,DP[i-1][j]表示第i个人影响不到前面人的等待总时间
//sum[i]是前i个人的等待时间总和 } }
code
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cstring> #define maxn 205 #define maxt 40010 #define re register using namespace std; int DP[maxn][maxt],sum[maxn],n; struct POP{ int a,b; }stu[maxn]; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } bool cmp(POP A,POP B) { return A.b>B.b; } int main() { n=read(); for(re int i=1;i<=n;++i) { stu[i].a=read(); stu[i].b=read(); } memset(DP,127,sizeof(DP)); sort(stu+1,stu+n+1,cmp); for(re int i=1;i<=n;++i) { sum[i]=sum[i-1]+stu[i].a; }jiufftts DP[0][0]=0; for(re int i=1;i<=n;++i) { for(re int j=0;j<=sum[i];++j) { if(j>=stu[i].a) DP[i][j]=min(DP[i][j],max(DP[i-1][j-stu[i].a],j+stu[i].b)); DP[i][j]=min(DP[i][j],max(DP[i-1][j],sum[i]-j+stu[i].b)); } } int ans=2147483647; for(re int i=1;i<=sum[n];++i) ans=min(ans,DP[n][i]); printf("%d",ans); return 0; }