次元传送门:洛谷P2577
思路
首先贪心是必须的
我们能感性地理解出吃饭慢的必须先吃饭(结合一下生活)
因此我们可以先按吃饭时间从大到小排序
然后就能自然地想到用f[i][j][k]表示前i个人在第一个窗口排队用了j时间 在第二个窗口排队用了k时间
然后就自然地炸空间了
所以我们要降维
因为我们可以由第一个窗口推出第二个窗口所用时间 所以我们可以改原来的数组为:
f[i][j]表示前i个人 在第一个窗口用了j时间 得到的所有前i个人吃完饭的最短时间
如何用第一个窗口推出第二个窗口呢?
显而易见 第一个窗口排队用了j时间+第二个窗口排队用了k时间为前i个人排队用的总时间
那么我们可以用前缀和维护一个总时间sum[i]表示前i个人用的总时间
那么k=sum[i]-j 解决
状态转移方程:
f[i][j]=min(f[i][j],max(f[i-1][j-p[i].a],j+p[i].b));//第i个人在第一窗口 //f[i-1][j-p[i].a]表示第i个人排队+吃饭的时间比第i-1个人短 所以不影响 //j+p[i].b即有影响 前i个人排队时间+第i个人吃饭时间 f[i][j]=min(f[i][j],max(f[i-1][j],sum[i]-j+p[i].b));//第i个人在第二窗口 //同理 sum[i]-j=k即第二个窗口排队总时间
代码
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; #define maxn 220 #define INF 1e9+7 struct P { int a; int b; }p[maxn]; int n; int sum[maxn]; int f[maxn][maxn*maxn]; bool cmp(P a,P b) { return a.b>b.b; } int main() { memset(f,0x3f,sizeof(f)); cin>>n; for(int i=1;i<=n;i++) cin>>p[i].a>>p[i].b; sort(p+1,p+1+n,cmp);//排序 for(int i=1;i<=n;i++) sum[i]=sum[i-1]+p[i].a;//前缀和 f[0][0]=0;//初始化 for(int i=1;i<=n;i++)//枚举第几个人 for(int j=0;j<=sum[i];j++)//枚举排队时间 { if(j>=p[i].a) f[i][j]=min(f[i][j],max(f[i-1][j-p[i].a],j+p[i].b)); f[i][j]=min(f[i][j],max(f[i-1][j],sum[i]-j+p[i].b)); } int ans=INF; for(int i=0;i<=sum[n];i++)//查询时间 ans=min(ans,f[n][i]); cout<<ans; }