题目概述
有n个人,2个窗口,每个人到1窗口和2窗口的打饭时间和吃饭时间都是一样的。问所有人吃好饭的最早时间。
解题报告
如果只有一个窗口,那就是从大到小排吃饭时间,然后按照这个顺序排队(某基础贪心题,好像叫突击战)。但是两个窗口怎么办?原来的贪心策略肯定还要保留,但是如何实现两个窗口呢?由于n很小,我们会想到DP。但是状态好像很难定义,因为哪个人是哪个队的如果要存下来是不可能的。思考一下我们发现哪个人是哪个队的不需要知道,只需要知道某个队目前的最大时间就行了!定义f[i][j]表示前i个中,第一个窗口的打饭总时间为j的最优解。那么对于i,分为两种状态(sum[i]表示1~i打饭时间的总和):
1.放窗口2
上一状态:max(f[i-1][j],sum[i-1]-j+i的打饭时间+i的吃饭时间)
其中f[i-1][j]是上一状态的最优解,后面那些是i放窗口2的代价。
2.放窗口1
上一状态:max(f[i-1][j-i的打饭时间],j-i的打饭时间+i的打饭时间+i的吃饭时间)
其中f[i-1][j-i的打饭时间]是上一状态的最优解,后面那些是i放窗口1的代价。
示例程序
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200,maxm=40000,MAXINT=((1<<30)-1)*2+1;
int n,f[maxn+5][maxm+5],sum[maxn+5],ans;
struct Student
{
int a,b;
bool operator < (const Student &a) const {return b>a.b;}
};
Student s[maxn+5];
int main()
{
freopen("program.in","r",stdin);
freopen("program.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d%d",&s[i].a,&s[i].b);
sort(s+1,s+1+n);
for (int i=1;i<=n;i++) sum[i]=sum[i-1]+s[i].a;
memset(f,63,sizeof(f));
f[0][0]=0;
for (int i=1;i<=n;i++)
for (int j=0;j<=sum[i];j++)
{
f[i][j]=min(f[i][j],max(f[i-1][j],sum[i-1]-j+s[i].a+s[i].b));
if (s[i].a<=j) f[i][j]=min(f[i][j],max(f[i-1][j-s[i].a],j+s[i].b));
}
ans=MAXINT;
for (int j=0;j<=sum[n];j++) ans=min(ans,f[n][j]);
printf("%d\n",ans);
return 0;
}