*【计蒜客 - 蓝桥训练】人以群分(二分 + dp)
題干:
某班有?nn?個(gè)同學(xué),每個(gè)同學(xué)有一個(gè)外向程度?a_iai?。由于要進(jìn)行某個(gè)活動(dòng),需要把他們分成若干個(gè)小組,每個(gè)小組的人數(shù)至少為?mm?人。不同外向程度的人在一個(gè)小組會(huì)產(chǎn)生不開(kāi)心值,定義一個(gè)小組的不開(kāi)心值為組內(nèi)成員外向程度最大值和最小值的差,一個(gè)班級(jí)的不開(kāi)心值為所有小組不開(kāi)心值的最大值。
那么問(wèn)題來(lái)了,如何分組使得班級(jí)的不開(kāi)心值最小,請(qǐng)你求出這個(gè)最小的班級(jí)不開(kāi)心值。
輸入格式
第一行兩個(gè)整數(shù)?n,mn,m,分別表示人數(shù)和每個(gè)小組最少的人數(shù)要求。
第二行?nn?個(gè)整數(shù)?a_iai?,表示每個(gè)同學(xué)的外向程度。
輸出格式
一個(gè)整數(shù),表示最小的班級(jí)不開(kāi)心值。
數(shù)據(jù)范圍
對(duì)于?30\%30%?的數(shù)據(jù):1\le m \le n \le 201≤m≤n≤20,1\le a_i \le 1001≤ai?≤100。
對(duì)于?60\%60%?的數(shù)據(jù):1\le m \le n\le 10001≤m≤n≤1000,1\le a_i \le 10001≤ai?≤1000。
對(duì)于?100\%100%?的數(shù)據(jù):1\le m\le n \le 5\cdot10^51≤m≤n≤5?105,1\le a_i \le 10^91≤ai?≤109。
樣例解釋
第一個(gè)樣例,只要每個(gè)人各自一個(gè)組,不開(kāi)心值就都是?00。
第二個(gè)樣例,最佳的分組情況為:9,119,11?一個(gè)組,6,3,56,3,5?一個(gè)組,兩個(gè)組的不開(kāi)心值分別為?22?和?33,那么班級(jí)的不開(kāi)心值為?33。
樣例輸入1復(fù)制
5 1 2 4 6 8 10樣例輸出1復(fù)制
0樣例輸入2復(fù)制
5 2 9 11 6 3 5樣例輸出2復(fù)制
3解題報(bào)告:
這題關(guān)鍵在于怎么check。我們用dp[i]代表:對(duì)前i個(gè)人進(jìn)行劃分,最后一個(gè)可以進(jìn)行分組的(可以被塞進(jìn)某個(gè)分組的)人的編號(hào)。
考慮最后一個(gè)人,判斷當(dāng)前第i個(gè)人能不能作為當(dāng)前分組的最后一個(gè)人,這就需要找之前分完組后第一個(gè)沒(méi)組可歸的人的編號(hào)?即dp[i-m]+1。如果能作為最后一個(gè)人,那dp[i]=i,否則就是dp[i-1]。
?
AC代碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define ll long long #define pb push_back #define pm make_pair using namespace std; const int MAX = 6e5 + 5; int n, m; int a[MAX], dp[MAX]; bool ok(int x) {// dp[0~m-1]=0for (int i = m; i <= n; i++) {if (a[i] - a[dp[i - m] + 1] <= x) dp[i] = i;else dp[i] = dp[i - 1];}return dp[n] == n; } int main() {cin>>n>>m;for (int i = 1; i <= n; i++) scanf("%d", a+i);sort(a + 1, a + n + 1);int l = 0, r = 1e9;int mid = (l + r) >> 1;while (l < r) {mid = (l + r) >> 1;if (ok(mid)) r = mid;else l = mid + 1;}printf("%d\n", l);return 0; }另一個(gè)dp做法:
所有數(shù)據(jù)首先經(jīng)過(guò)排序。 我們?nèi)绻胐p[i]表示前i個(gè)數(shù)字在猜測(cè)答案x的條件下,是否能夠劃分成比m大的子集和。那么dp[i]可以去把l…r-1的一串?dāng)?shù)字染成true,l=i+m,r是第一個(gè)不滿(mǎn)足a[r]-a[i+1]<=x的位置(當(dāng)然r可能小于或等于l,這時(shí)候不染色)。
?每check一次dp一次,返回的是dp[n]是否為true。
?然而這樣的話(huà)dp最差是n^2效率,可以想到用樹(shù)狀數(shù)組+掃描線(xiàn)或者線(xiàn)段樹(shù)來(lái)改進(jìn)。但是五十萬(wàn)的數(shù)據(jù)不允許兩個(gè)log存在。實(shí)際上可以發(fā)現(xiàn)由于我們的點(diǎn)查詢(xún)是從左到右連續(xù)的,所以沒(méi)必要搞樹(shù)狀數(shù)組,直接掃描線(xiàn)就行了,然后dp[i]就是i處的覆蓋區(qū)間數(shù),也就是掃描線(xiàn)數(shù)組的前綴和,前綴和大于0相當(dāng)于為true,否則為false。
原先做n^2的dp的話(huà),是如果dp[i]是true的話(huà),那么后面l~r-1也更新為true。
考慮到這個(gè)過(guò)程像是區(qū)間覆蓋,所以可以用端點(diǎn)處+1 ,-1,然后用前綴和來(lái)表示真正的dp值(這個(gè)前綴和也就是代碼里的pre,pre>0表示曾被true覆蓋過(guò),,也就是原先意義下的dp[i]=true)。。
只要i是可以達(dá)到的,l~r-1就可以從i處增加一個(gè)子集來(lái)達(dá)到。
鏈接
?
總結(jié)
以上是生活随笔為你收集整理的*【计蒜客 - 蓝桥训练】人以群分(二分 + dp)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: wincomm.exe - wincom
- 下一篇: 【POJ - 1961】Period(K