洛谷P2577 [ZJOI2005] 午餐

题意:

  • 有一群人,每个人有一个打饭时间和一个吃饭时间。现在有两个窗口可以打饭,求让所有人吃完饭的最少时间。

思路:

  • 首先想到贪心,在同一队里吃饭时间长的一定排在吃饭时间短的前面(证明略)

  • 然后DP, f [ i ] [ j ] 表示前i个人打完饭,第一队用时j的最小吃完饭时间(第二队用时可以用前 i 个人总时间减去 j )。转移就比较方便了,可以选择把 i 放在第一队或者第二队。

  • DP最难的还是表示状态,我最开始一直在想 f [ i ] [ j ] [ 0 / 1 ] 表示i个人在第一队j个人在第二队的情况下,两队的最小打饭时间,再有一个 g [ i ] [ j ] [ 0 / 1 ] 记吃完饭时间。然而这样无法避免后效性(状态转移都不行)

  • 贴个代码,以后还得多多练习:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 210;
int n, sum[N];
struct NODE{
    int x, y;
}a[N];
int f[N][N*N];

bool cmp(NODE x, NODE y)
{
    return x.y > y.y;
}

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i].x >> a[i].y;
    sort(a+1, a+n+1, cmp);
    sum[1] = a[1].x;
    for (int i = 2; i <= n; i++)
        sum[i] = sum[i-1]+a[i].x;
    memset(f, 0x7f, sizeof(f));
    f[0][0] = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= sum[i]; j++){
            int k = sum[i]-j;
            if (j >= a[i].x) f[i][j] = min(f[i][j], max(f[i-1][j-a[i].x], j+a[i].y));
            if (k >= a[i].x) f[i][j] = min(f[i][j], max(f[i-1][j], k+a[i].y));
        }
    int ans = 0x7f7f7f7f;
    for (int i = 0; i <= sum[n]; i++)
        ans = min(f[n][i], ans);
    cout << ans;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyyxyyx/article/details/81233025