洛谷 P2577 [ZJOI2005]午餐【贪心】【dp】


题目:

传送门


题意:

一共有 n n n个人,每个人都有两个信息:打饭时间和吃饭时间
现在有两个窗口可以打饭,问最短多快能使所有人都吃完饭


分析:

假如我们现在已经确定了两个窗口分别有哪些人,那么要想使得总时间最短,我们就应该把吃得慢的人尽可能的往前安排
这样我们要考虑的就是如何安排这两个窗口的人,设 f i , j , k f_{i,j,k} fi,j,k表示前 i i i个人中,在第一个窗口的排队时间为 j j j,第二个窗口的排队时间为 k k k时最短耗时
但这样明显是要 M L E MLE MLE的,所以我们必须要省掉一维空间,我们发现 j + k j+k j+k的值在每个 i i i的时候都是固定的,这样的话我们就可以用前缀和维护每个 j + k j+k j+k的值,留下一个 j j j,而 k k k s i − j s_i-j sij表示
方程呢,我们就考虑当前的 f i , j f_{i,j} fi,j会由什么转移过来,一个是前面 i − 1 i-1 i1这些人耗得时间会比第 i i i个人的总耗时都要多,这样我们就直接由 f i − 1 , j − a f_{i-1,j-a} fi1,ja转移;另一个当现在是吃得最慢的了,那没办法,总时间就是排队等待的时间加上第 i i i个人吃饭的时间
f i , j = m a x ( f i − 1 , j − a , j + b ) f_{i,j}=max(f_{i-1,j-a},j+b) fi,j=max(fi1,ja,j+b)
当然,这只是第 i i i个人在一号窗口排队的情况,还有在二号窗口的也是同理


代码:

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<cmath>
#include<vector>
#define LL long long 
using namespace std;
inline LL read() {
    
    
    LL d=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){
    
    if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){
    
    d=d*10+s-'0';s=getchar();}
    return d*f;
}
struct node{
    
    
	int a,b;
}x[205];
bool cmp(node a,node b) {
    
    return a.b>b.b;}
int sum[205];
int f[205][40005];
int main()
{
    
    
	int n=read();
	for(int i=1;i<=n;i++) x[i].a=read(),x[i].b=read();
	sort(x+1,x+1+n,cmp);
	for(int i=1;i<=n;i++) sum[i]=sum[i-1]+x[i].a;
	for(int i=0;i<=n;i++)
	  for(int j=0;j<=sum[n];j++)
	    f[i][j]=99999999;
	f[0][0]=0;
	for(int i=1;i<=n;i++)
	{
    
    
		for(int j=x[i].a;j<=sum[i];j++) f[i][j]=min(f[i][j],max(f[i-1][j-x[i].a],j+x[i].b));
		for(int j=0;j<=sum[i];j++) f[i][j]=min(f[i][j],max(f[i-1][j],sum[i]-j+x[i].b));
	}
	int ans=2147483647;
	for(int i=0;i<=sum[n];i++) ans=min(ans,f[n][i]);
	cout<<ans;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35786326/article/details/103539990