1. 程式人生 > >#dp# 2018-2019 ACM-ICPC, NEERC, Southern Subregional Contest, Qualification Stage

#dp# 2018-2019 ACM-ICPC, NEERC, Southern Subregional Contest, Qualification Stage

K. Medians and Partition

Description:

將一組數恰好分割成多組數,對每組數進行排序後,使得每組數的中位數大於等於m,求解最多可以分成多少組數

Solution:

ok[i][j]: 在 [i, j] 這段區間裡的中位數是否符合要求,即 ok[i][j] = 1 代表 [i, j] 這段區間的中位數大於等於m
dp[i]:以 i 位置為結尾的區間劃分最多可以劃分成多少個區間段

先處理出任意一個子區間是否合法,即中位數是否大於等於m。
對於每個結尾的位置 i ,列舉位置每個小段區間的端點 j(也就是這些合法的 j 位置都可以將 [1, n] 這個完整區間分割成多個小段區間)
轉移方程:dp[i] = max(dp[i], dp[j] + 1);

最後維護一下最大值作為答案即可。

其實對於求解任意一個子區間的中位數時,並不需要具體算出中位數是多少,只要能判斷出與 m 的關係即可,比 m 大還是小。

程式碼:

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue> #include <stack> #include <set> #include <map> #define fi first #define se second #define mst(a, b) memset(a, b, sizeof(a)) using namespace std; typedef long long LL; typedef pair<int, int> PII; const int INF = 0x3f3f3f3f; const double eps = 1e-9; const
int Mod = 1e9 + 7; const double pi = acos(-1); const int MaxN = 5e3 + 5; int a[MaxN]; int ok[MaxN][MaxN]; int dp[MaxN]; int main() { int n, m; scanf("%d %d", &n, &m); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); for(int i = 1; i <= n; i++) { int cnt = 0; //記錄當前[i,j]這個區間內有多少個數比m小,用來判斷中位數與m的大小關係 for(int j = i; j <= n; j++) { if(a[j] < m) cnt++; int mid = (j - i + 2) / 2; //中位數是第幾個數,也就是“第幾大數” if(cnt < mid) ok[i][j] = 1; //說明此時的中位數一定大於等於m } } for(int i = 1; i <= n; i++) { dp[i] = -INF; for(int j = 0; j < i; j++) { if(ok[j+1][i]) dp[i] = max(dp[i], dp[j] + 1); } } printf("%d\n", max(dp[n], 0)); return 0; }