P2577 [ZJOI2005]午餐(dp)

原题: https://www.luogu.org/problemnew/show/P2577

题意:

n个人打饭,每个人有给出的打饭时间和吃饭时间。两个窗口,排队打饭,问最少的时间使所有人吃完饭。

解析:

首先是贪心选排队顺序。

因为一个队的总时间为:所有人的打饭时间,加最后一个人的吃饭时间,加如果有前面吃饭吃得慢的人的吃饭时间。分析最优解,假设最优解两队人顺序都是吃饭慢的先排队,此时调换顺序,则可能会变慢,但绝对不会变快。所以贪心成立。

接下来是两队的分配。

可以想到的dp为dp[i][j][k]表示:对于前i个人,第一队排队时间j,第二队排队时间k,此时的最优解(吃完饭)。为什么是排队时间?因为好实现,塞一个人进去直接加上他的排队时间,不用考虑吃饭的问题。

但是维度比较大,需要降维。这个也比较明显,因为前面i个人的总排队时间s知道了,k=s-j就省去一维了。


dp[i][j]代表:对于前i个人,第一队排队时间j时的最优解。

对于第i个人,如果塞到第一队,那么首先考虑i-1个人的情况dp[i-1][j-a[i]],还有第i个人吃饭的时间j+b[i]
如果塞到第二队,那么第一队的情况为dp[i-1][j],第二队第i人吃饭的情况s[i]-j+b[i]

代码:

#include<bits/stdc++.h>
using namespace std;

int n;
struct node{
    int a,b;
    bool operator <(const node &r)const{
        return b>r.b;
    }
} e[205];

int dp[205][205*205];
int s[205];

int main(){
    memset(dp,0x3f,sizeof dp);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&e[i].a,&e[i].b);
    }
    sort(e+1,e+1+n);
    for(int i=1;i<=n;i++)s[i]=e[i].a+s[i-1];
    dp[0][0]=0;
    for(int i=1;i<=n;i++){
        for(int j=e[i].a;j<=s[i];j++){
            dp[i][j]=min(dp[i][j],max(dp[i-1][j-e[i].a],j+e[i].b));
        }
        for(int j=0;j<=s[i];j++){
            dp[i][j]=min(dp[i][j],max(dp[i-1][j],s[i]-j+e[i].b));
        }
    }
    int ans=0x3f3f3f3f;
    for(int i=0;i<=s[n];i++){
        ans=min(ans,dp[n][i]);
    }
    printf("%d\n",ans);
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/86760045