2021HDU多校第二场 1008 I love exam 背包+DP

原题链接:https://acm.hdu.edu.cn/showproblem.php?pid=6968

题意

有n门课,m个学习资料,每个学习资料可以在bi天里提升ai的分数,每门课总分不能超过100分,同时可以挂p门课,学习总时长为t。问如何安排学习可以使得总分最大。

分析

题目相当绕,仔细分析一下,我们可以把每门课的学习资料先预处理出来,用 d p [ i ] [ j ] dp[i][j] dp[i][j]来表示第i门课获得j分最少需要多少天。然后就可以开始快乐dp了。

我们设一个状态 f [ i ] [ j ] [ k ] [ o ] f[i][j][k][o] f[i][j][k][o]分别表示第i门课,当前已经学习了j天,当前这门课得分为k,当前已经挂了o门课,状态的转移非常好表示
f [ i ] [ j ] [ k ] [ o ] = m a x ( f [ i − 1 ] [ j − d p [ i ] [ k ] ] [ k p r e ] [ o ] + k ( 不 挂 科 ) f[i][j][k][o]=max(f[i-1][j-dp[i][k]][k_{pre}][o]+k(不挂科) f[i][j][k][o]=max(f[i1][jdp[i][k]][kpre][o]+k()

f [ i ] [ j ] [ k ] [ o ] = m a x ( f [ i − 1 ] [ j − d p [ i ] [ k ] ] [ k p r e ] [ o − 1 ] + k ( 挂 科 ) f[i][j][k][o]=max(f[i-1][j-dp[i][k]][k_{pre}][o-1]+k(挂科) f[i][j][k][o]=max(f[i1][jdp[i][k]][kpre][o1]+k()

如果直接枚举转移会超时,在记录一个 m x [ i ] [ j ] [ o ] mx[i][j][o] mx[i][j][o]代表前i门课第j天挂科o次的最大分数,这个记录了 k ∈ [ 0 , 100 ] k∈[0,100] k[0,100]之内的最值,因此转移的时候可以直接转移上一层的最大值,即

f [ i ] [ j ] [ k ] [ o ] = m x [ i − 1 ] [ j − d p [ i ] [ k ] ] [ o ] + k ( 不 挂 科 ) f[i][j][k][o]=mx[i-1][j-dp[i][k]][o]+k(不挂科) f[i][j][k][o]=mx[i1][jdp[i][k]][o]+k()

f [ i ] [ j ] [ k ] [ o ] = m x [ i − 1 ] [ j − d p [ i ] [ k ] ] [ o − 1 ] + k ( 挂 科 ) f[i][j][k][o]=mx[i-1][j-dp[i][k]][o-1]+k(挂科) f[i][j][k][o]=mx[i1][jdp[i][k]][o1]+k()

还有就是处理-1的情况,我们对所有门课 ∑ m i n d p [ i ] [ 60..100 ] > t \sum mindp[i][60..100]>t mindp[i][60..100]>t那么必定无解

赛中数组开小导致一直没过,调到自闭,我是演员

Code

#include <bits/stdc++.h>
#define lowbit(i) i & -i
#define Debug(x) cout << x << endl
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const ll INF = 1e18;
const int N = 3e5 + 10;
const int M = 1e6 + 10;
const int MOD = 998244353;
int dp[55][105];
int a[55][15005], b[55][15005];
int f[55][505][105][5], mx[55][505][5];
int num[55], t, p;
void solve() {
    int T; cin >> T; while (T--) {
        int n; cin >> n;
        for (int i = 1; i <= n; i++) num[i] = 0;
        memset(a, 0, sizeof a);
        memset(b, 0, sizeof b);
        memset(mx, -0x3f, sizeof mx);
        memset(f, -0x3f, sizeof f);
        map<string, int> mp;
        for (int i = 1; i <= n; i++) {
            string s;
            cin >> s;
            mp[s] = i;
        }
        int m; cin >> m;
        for (int i = 1; i <= m; i++) {
            string s; cin >> s;
            int x, y; cin >> x >> y;
            a[mp[s]][++num[mp[s]]] = x;
            b[mp[s]][num[mp[s]]] = y;
        }
        cin >> t >> p;
        //p = min(n, p);
        memset(dp, 0x3f, sizeof dp);
        for (int i = 1; i <= n; i++) {
            dp[i][0] = 0;
            for (int j = 1; j <= num[i]; j++) {
                for (int k = 100; k >= a[i][j]; k--) {
                    dp[i][k] = min(dp[i][k], dp[i][k - a[i][j]] + b[i][j]);
                }
            }
        }
        vector<int> ve;
        for (int i = 1; i <= n; i++) {
            int Min = 1e9;
            for (int j = 60; j <= 100; j++) {
                Min = min(Min, dp[i][j]);
            }
            ve.push_back(Min);
        }
        sort(ve.begin(), ve.end());
        int sum = 0;
        for (int i = 0; i < min((int) ve.size(), n - p); i++) {
            sum += ve[i];
        }
        if (sum > t) {
            cout << -1 << endl;
            continue;
        }
        //cout << dp[1][60] << endl;
        int ans = 0;
        f[0][0][0][0] = mx[0][0][0] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= t; j++) {
                for (int k = 0; k <= 100; k++) {
                    for (int o = 0; o <= p; o++) {
                        if (k >= 60) {
                            if (j >= dp[i][k]) {
                                f[i][j][k][o] = max(f[i][j][k][o], mx[i-1][j-dp[i][k]][o] + k);
                            }
                        } else {
                            if (j >= dp[i][k] && o) {
                                f[i][j][k][o] = max(f[i][j][k][o], mx[i-1][j-dp[i][k]][o-1] + k);
                            }
                        }
                        mx[i][j][o] = max(mx[i][j][o], f[i][j][k][o]);
                        ans = max(ans, f[i][j][k][o]);
                    }
                }
            }
        }
        cout << ans << endl;
    }
}

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
    signed test_index_for_debug = 1;
    char acm_local_for_debug = 0;
    do {
        if (acm_local_for_debug == '$') exit(0);
        if (test_index_for_debug > 20)
            throw runtime_error("Check the stdin!!!");
        auto start_clock_for_debug = clock();
        solve();
        auto end_clock_for_debug = clock();
        cout << "Test " << test_index_for_debug << " successful" << endl;
        cerr << "Test " << test_index_for_debug++ << " Run Time: "
             << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
        cout << "--------------------------------------------------" << endl;
    } while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
    solve();
#endif
    return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值