[ZJOI2005]午餐

[ZJOI2005]午餐

题面-洛谷P2577

思路

如果题中只有一个窗口,那么此题就是一道贪心水题(接水),显然贪心策略是让吃饭久的人先吃。但是此题有两个窗口,所以贪心后,需要动态规划决定让第\(i\)个人去第一或第二窗口吃饭。

dp状态\(dp[i][j]\)表示到第\(i\)个人,第一个窗口已排队总时间\(j\)时,最晚吃完饭的时间。而又因为到第\(i\)个人时,排队总时间可以前缀和预处理出\(sum[i]\),所以第二个窗口已排队总时间就是\(sum[i]-j\)

dp转移,第\(i\)个人去第一个窗口

\[dp[i][j]=MIN(MAX(dp[i-1][j-a[i].a], j+a[i].b), dp[i][j]) \]

去第二个窗口

\[dp[i][j]=MIN(MAX(dp[i-1][j], sum[i]-j+a[i].b), dp[i][j]) \]

因为\(dp\)存的是最晚时间,所以可以根据上一个\(dp​\)状态推出当前这个人,是否可以在上个一个人吃饭时,就打饭+吃饭完成。

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#define MAXN 220
#define MAX(A,B) ((A)>(B)?(A):(B))
#define MIN(A,B) ((A)<(B)?(A):(B))
using namespace std;
int n,sum[MAXN],ans=0x3f3f3f3f;
int dp[MAXN][MAXN*MAXN];
struct nod{
    int a,b;
    bool operator < (const nod &m){
        return b > m.b;
    }
} a[MAXN];
int main()
{
    scanf("%d", &n);
    for(int i=1;i<=n;++i)
        scanf("%d %d", &a[i].a, &a[i].b);
    sort(a+1, a+1+n);
    for(int i=1;i<=n;++i)
        sum[i]=sum[i-1]+a[i].a;
    memset(dp, 0x3f, sizeof(dp));
    dp[0][0]=0;
    for(int i=1;i<=n;++i)
    for(register int j=0;j<=sum[i];++j){
        if(j-a[i].a>=0) dp[i][j]=MIN(MAX(dp[i-1][j-a[i].a], j+a[i].b), dp[i][j]); // 去一号
        dp[i][j]=MIN(MAX(dp[i-1][j], sum[i]-j+a[i].b), dp[i][j]); // 去二号
    }
    for(int i=0;i<=sum[n];++i)
        ans=MIN(ans, dp[n][i]);
    printf("%d", ans);
    return 0;
}
posted @ 2019-05-13 13:51  Santiego  阅读(173)  评论(0编辑  收藏  举报