Codeforces 1487E. Cheap Dinner(DP + multiset)

传送门


题目大意

给出四个大小为 n i ( 1 ≤ n i ≤ 150000 ) n_i(1 \leq n_i \leq 150000) ni(1ni150000)序列,给出三种制约关系,即 1    2 1~~2 1  2 2    3 2~~3 2  3 3    4 3~~4 3  4两两数组之间都有 m i ( 0 ≤ m i ≤ 200000 ) m_i(0 \leq m_i \leq 200000) mi(0mi200000)对数不能同时选取(保证给出的下标对不会重复)。从四个序列中选出四个不存在制约关系的数且使得四者之和最小,输出最小的和(若无法得到四个数输出-1)

解题思路

一开始自己想着用排序、容器、四指针等等方式,发现都做不了,看了一眼题解,发现是DP,看到下面说需要数据结构插入删除且动态维护最值就秒懂了。

对于最裸的DP,无非就是对于当前序列的某个数需要从上一个序列中不存在制约关系的数中选出一个最小的,然后从上到下更新状态即可,但是这个时间复杂度是 O ( n 2 ) O(n^2) O(n2)的。

考虑使用支持插入、删除的数据结构优化DP,可以使用 m u l t i s e t multiset multiset,能维护的原因中关键的两点是:

  • 对于当前序列的某个位置的数,我们只需要知道和它制约的上个序列的数的大小。显然使用 m u l t i s e t multiset multiset维护时删掉就行,为了不影响后面,可以状态转移后再把它们插入 m u l t i s e t multiset multiset
  • 有制约关系的数对是有限个的,因此总体的时间复杂度是 O ( ( ∑ n i + ∑ m j ) ∗ l o g n O((\sum{n_i} + \sum{m_j}) * logn O((ni+mj)logn
#include <bits/stdc++.h>

using namespace std;
#define ENDL "\n"
typedef long long ll;
const int maxn = 2e5 + 10;
const int inf = 0x3f3f3f3f;

int n[4], a[4][maxn];
int d[4][maxn];
vector<int> g[maxn];
multiset<int> ms;

int main() {
    
    
    // ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    for (int i = 0; i < 4; i++) scanf("%d", &n[i]);
    for (int i = 0; i < 4; i++) {
    
    
        for (int j = 1; j <= n[i]; j++) scanf("%d", &a[i][j]);
    }
    for (int i = 1; i <= n[0]; i++) d[0][i] = a[0][i], ms.insert(a[0][i]);
    for (int i = 1, cnt; i < 4; i++) {
    
    
        scanf("%d", &cnt);
        for (int j = 1, x, y; j <= cnt; j++) {
    
    
            scanf("%d%d", &x, &y);
            g[y].push_back(x);
        }
        for (int j = 1; j <= n[i]; j++) {
    
    
            if (g[j].size()) {
    
    
                for (auto k : g[j]) {
    
    
                    if (d[i - 1][k]) {
    
    
                        auto pos = ms.find(d[i - 1][k]);
                        if (pos != ms.end()) ms.erase(pos);
                    }
                }
                d[i][j] = ms.empty() ? 0 : a[i][j] + *ms.begin();
                for (auto k : g[j])
                    if (d[i - 1][k]) ms.insert(d[i - 1][k]);
                g[j].clear();
            } else
                d[i][j] = ms.empty() ? 0 : a[i][j] + *ms.begin();
        }
        ms.clear();
        if (i < 3)
            for (int j = 1; j <= n[i]; j++)
                if (d[i][j]) ms.insert(d[i][j]);
        // for (auto k : ms) cout << k << " ";
        // cout << endl;
    }
    int ans = inf;
    for (int i = 1; i <= n[3]; i++)
        if (d[3][i]) ans = min(ans, d[3][i]);
    printf("%d\n", ans >= inf ? -1 : ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44691917/article/details/115555336