哈卡雜貨店裡,只有一排貨架,擺放著各式商品。然而裡頭的哈卡老闆有強迫症,他想要讓這個貨架的商品價格,從商品編號1~N,至少有K件商品價格是按序遞增的。哈卡是不會調降價錢的,哈卡也想把力氣省下來,哈卡決定以某件商品加一元的方式,直到貨架上的商品有至少K個按序遞增。
試問哈卡最少需要做幾次“加某件商品一元”這個動作,才能讓貨架上的N件商品至少有K件的價格按序遞增?
以上題敘的意思也就是:
給你一個長度為N的序列,你可以對任一項加一,求最少要加幾次才存在一個子序列長度為K,且值非嚴格遞增。(子序列是不用連續的)
$K\le N\le 200, a_i\le 10^9$
這題子測資切得蠻好的 所以我應該會分開寫
subtask1: $N=K$
等價於要讓整個序列變得非嚴格遞增
就只要貪心的把當前項加到不小於前面的就好
$\mathcal{O}(N)$
subtask2: $N\le 40$
這筆我不知道要幹嘛 跳過
subtask3: $a_i\le 200$
考慮$dp[K][C][N]$代表以第$N$個為最後一項,目前長度為$K$,且已經把$a_N$弄成$C$了
這樣轉移式就是
$$dp[K][C][N]=\min_{A\le C, B<N} dp[K-1][A][B]+C-a_N (\text{ if } C>a_N)$$
只要透過適當轉移順序 順便紀錄點東西就能做到$\mathcal{O}(1)$轉移
所以複雜度$\mathcal{O}(KNC)$
subtask 4: No other constraints
直接拿subtask3的dp 把序列給離散化後就能變回第三題的樣子 離散化複雜度$O(NlogN)$
整體複雜度$\mathcal{O}(NlogN+KN^2)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
|
#include <bits/stdc++.h> using namespace std;
#define EmiliaMyWife ios::sync_with_stdio(0); cin.tie(NULL); #define mem(i,j) memset(i,j,sizeof (i)); using ll = int64_t; using ull = uint64_t; using ld = long double; using uint = uint32_t; const double EPS = 1e-8; const int INF = 0x3F3F3F3F; const ll LINF = 4611686018427387903; const int MOD = 1e9+7;
const int N = 201; ll dp[N][N][N];
signed main() { EmiliaMyWife int n, k; ll ans = LINF; cin >> n >> k; vector<int> arr(n + 1), owo(n); for(int i = 0; i < n; i++) cin >> arr[i + 1], owo[i] = arr[i + 1]; for(int i = 0; i < N; i++) for(int j = 0; j < N; j++) for(int k = 0; k < N; k++) dp[i][j][k] = LINF;
sort(owo.begin(), owo.end()); owo.erase(unique(owo.begin(), owo.end()), owo.end()); for(int len = 1; len <= k; len++) { vector<ll> mn(n + 1, LINF); if(len == 1) mn[0] = 0; for(int j = 0; j < owo.size(); j++) { for(int i = 1; i <= n; i++) { if(owo[j] >= arr[i]) dp[i][j][len] = mn[i - 1] + owo[j] - arr[i]; mn[i] = min({mn[i], mn[i - 1], dp[i][j][len - 1]}); if(len == k) ans = min(ans, dp[i][j][len]); } } } cout << ans << '\n';
return 0; }
|