2021HDU多校第三场 1009 Rise in Price 思维+乱搞

本文解析了一道关于动态规划问题的ACM题目,涉及如何在两个矩阵中最大化从左上角到右下角的钻石售卖价值,通过限制状态数量并采用归并排序合并策略,解决指数级状态爆炸问题,代码展示了如何利用归并排序合并dp数组。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

题意

有两个n*n的矩阵a,b分别代表每个格子的钻石数量和提升的单价,我们只能从(1,1)开始走到(n,n),问售卖钻石的最大价值是多少。

分析

赛中往DP和网络流方向去想了,很明显发现这个状态根本无法全部表示,记录到当前为止拿到的宝石数量和提升的价格是不可能的,因为这是指数提升的状态。根据题解中描述的,我们只需要取最满足条件的前K个状态保存一下,接着进行合并。

合并的方法也非常简单,只需要利用归并排序的思想,我们将 宝 石 数 ∗ 单 价 宝石数*单价 高的状态往前放一放,只需要O(n)的合并复杂度,粗略计算一下复杂度大约是 O ( T ∗ n 2 K ) O(T*n^2K) O(Tn2K),因此K取100左右就可以了。

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<ll, ll> PII;
const ll INF = 1e18;
const double eps = 1e-3;
const int N = 1e6 + 10;
const int M = 1e6 + 10;
const int MOD = 998244353;
vector<PII> f[105][105];
int a[105][105], b[105][105];
bool cmp(PII x, PII y) {
    return x.fi * x.se >= y.fi * y.se;
}
void merge(vector<PII> &F, vector<PII> f1, vector<PII> f2) {
    int l = 0, r = 0;
    int SIZE = 105;
    while (l < f1.size() && r < f2.size() && F.size() < SIZE) {
        if (f1[l].fi * f1[l].se >= f2[r].fi * f2[r].se) {
            F.push_back({f1[l].fi, f1[l].se});
            l++;
        } else {
            F.push_back({f2[r].fi, f2[r].se});
            r++;
        }
    }
    while (l < f1.size() && F.size() < SIZE) {
        F.push_back({f1[l].fi, f1[l].se});
        l++;
    }
    while (r < f2.size() && F.size() < SIZE) {
        F.push_back({f2[r].fi, f2[r].se});
        r++;
    }
    sort(F.begin(), F.end(), cmp);
}
void solve() {
    int T; cin >> T; while (T--) {
        int n; cin >> n;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                cin >> a[i][j];
                f[i][j].clear();
            }
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                cin >> b[i][j];
            }
        }
        f[1][1].push_back({a[1][1], b[1][1]});
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                if (i == 1 && j == 1) continue;
                else if (i == 1) f[i][j] = f[i][j-1];
                else if (j == 1) f[i][j] = f[i-1][j];
                else merge(f[i][j], f[i-1][j], f[i][j-1]);
                for (auto &it : f[i][j]) it.fi += a[i][j], it.se += b[i][j];
            }
        }
        ll ans = 0;
        for (int i = 0; i < f[n][n].size(); i++) {
            ans = max(ans, f[n][n][i].fi * f[n][n][i].se);
        }
        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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值