P2577 [ZJOI2005]午餐

神仙dp题。

先做一个贪心的排序:吃得慢的先去打饭。即按b从大到小排序。

接下来再做dp:

我们可以这么定义状态:\(dp[i][j][k]\)表示前\(i\)个人,第一队的人打饭用了\(j\)时间,第二队的人打饭用了\(k\)时间,再吃完饭的最少时间。

但是这样开的数组是\(200 \times 40000 \times 40000\),铁定MLE。

我们发现:前\(i\)个人打饭的时间是固定的。如果对前\(i\)个人打饭时间做前缀和,就有\(sum[i] = j + k\)。所以直接降了一维。

接下来就不懂了。

我问了题解区的dalao,等dalao指教ing。懂了再回来写。

代码:

  • 二维dp
#include<bits/stdc++.h>
using std::cin;
using std::cout;
using std::endl;
const int maxn = 201;
struct Nodes {
    int a, b;
} s[maxn];
int sum[maxn];
int dp[maxn][maxn * maxn];
int n, m;
bool cmp(Nodes A, Nodes B) {
    return A.b > B.b;
}
int main() {
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> s[i].a >> s[i].b;
    std::sort(s + 1, s + n + 1, cmp);
    for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + s[i].a;
    memset(dp, 0x3f, sizeof dp);
    dp[0][0] = 0;
    for(int i = 1; i <= n; i++) {
        for(int j = 0; j <= sum[i]; j++) {
            if(j >= s[i].a) dp[i][j] = std::min(dp[i][j], std::max(dp[i - 1][j - s[i].a], j + s[i].b));
            dp[i][j] = std::min(dp[i][j], std::max(dp[i - 1][j], (sum[i] - j) + s[i].b));
        }
    }
    int ans = 0x3f3f3f3f;
    for(int i = 0; i <= sum[n]; i++) {
        ans = std::min(ans, dp[n][i]);
    }
    cout << ans << endl;
    return 0;
}
posted @ 2019-06-01 14:42  Garen-Wang  阅读(98)  评论(0编辑  收藏  举报