【題解】擺渡車
題目描述
有n名同學要乘坐擺渡車從人大附中前往人民大學,第i位同學在第ti分鐘去等車。只有一輛擺渡車在工作,但擺渡車容量可以視為無限大。擺渡車從人大附中出發、把車上的同學送到人民大學、再回到人大附中(去接其他同學),這樣往返一趟總共花費m分鐘(同學上下車時間忽略不計)。擺渡車要將所有同學都送到人民大學。
凱凱很好奇,如果他能任意安排擺渡車出發的時間,那麽這些同學的等車時間之和最小為多少呢?
註意:擺渡車回到人大附中後可以即刻出發。
輸入格式
第一行包含兩個正整數n,m,以一個空格分開,分別代表等車人數和擺渡車往返一趟的時間。
第二行包含n個正整數,相鄰兩數之間以一個空格分隔,第i個非負整數ti代表第i個同學到達車站的時刻。
輸出格式
一行,一個整數,表示所有同學等車時間之和的最小值(單位:分鐘)。
輸入樣例一
5 1
3 4 4 3 5
輸出樣例一
0
輸入樣例二
5 5
11 13 1 5 5
輸出樣例二
4
樣例說明
樣例一說明:
同學1和同學4在第3分鐘開始等車,等待0分鐘,在第3分鐘乘坐擺渡車出發。擺渡車在第4分鐘回到人大附中。
同學2和同學3在第4分鐘開始等車,等待0分鐘,在第4分鐘乘坐擺渡車出發。擺渡車在第5分鐘回到人大附中。
同學5在第5分鐘開始等車,等待0分鐘,在第5分鐘乘坐擺渡車出發。自此所有同學都被送到人民大學。總等待時間為 0。
樣例二說明:
同學3在第1分鐘開始等車,等待0分鐘,在第1分鐘乘坐擺渡車出發。擺渡車在第6分鐘回到人大附中。
同學4和同學5在第5分鐘開始等車,等待1分鐘,在第6分鐘乘坐擺渡車出發。擺渡車在第11分鐘回到人大附中。
同學1在第11分鐘開始等車,等待2分鐘;同學2在第13分鐘開始等車,等待0分鐘。他/她們在第13分鐘乘坐擺渡車出發。自此所有同學都被送到人民大學。
總等待時間為4。可以證明,沒有總等待時間小於4的方案。
數據規模
對於10%的數據,n≤10,m=1,0≤ti≤100。
對於30%的數據,n≤20,m≤2,0≤ti≤100。
對於50%的數據,n≤500,m≤100,0≤ti≤10000。
另有20%的數據,n≤500,im≤10,0≤ti≤1000000。
對於100%的數據,n≤500,m≤100,0≤ti≤4000000。
題解
話說當年考場上做這道題時感覺毫無頭緒,考完noip後也一直把這題棄著,直到放假了有時間做,發現也就這麽一回事。
洛谷上評分是藍題,個人感覺頂多綠題。。。
回到正題,觀察這一題的數據範圍,可以看出$n,m$都很小,那我們可以考慮從這方面進行dp。
首先我們按照升序對$a[i]$進行排序(由於個人習慣,這裏這裏及下文用$a[i]$表示$t[i]$)。
那我們設$dp[i][j]$為第$i$個同學在$a[i]+j$的時間點出發時,第$1$到第$i$個同學的等待時間之和的最小值,可以想到$0 \leqslant j < m$。(如果$m \leqslant j$的話,那正在等待的同學實際上可以在$a[i]+j-m$的時間點出發,不需要繼續等下去,故$0 \leqslant j < m$)
容易想到,$dp[i][j]=min \left \{dp[ii][jj]+wait(i, j, ii) \right \}$($0 \leqslant ii < i$),其中$wait(i, j, ii)$為第$ii$到第$i$個同學的等待時間之和。
根據上文對$dp[i][j]$的定義,可以得到:$a[i] + j < a[i + 1]$同理$a[ii] + jj < a[ii + 1]$。同時,根據這個方程還可以得到$a[ii] + jj + m \leqslant a[i] + j$。
同時我們也可以想到,如果設$s[i] = \sum_{j = 1}^{i} a[j]$,那麽$wait(i, j, ii) = (a[i] + j) \times (i - ii) - (s[i] - s[ii])$
此時我們可以得到一個時間復雜度為$O(n^{2}m^{2})$的程序,是可以拿70pts的。
#include <iostream> #include <cstring> #include <algorithm> #define MAX_N (500 + 5) #define MAX_M (100 + 5) #define INF 0x7f7f7f7f #define WAIT(i, j, ii) ((a[(i)] + j) * ((i) - (ii)) - (s[(i)] - s[(ii)])) using namespace std; int n, m; int a[MAX_N], s[MAX_N]; int dp[MAX_N][MAX_M]; int ans = INF; int main() { cin >> n >> m; for(register int i = 1; i <= n; ++i) { cin >> a[i]; } sort(a + 1, a + n + 1); for(register int i = 1; i <= n; ++i) { s[i] = s[i - 1] + a[i]; } memset(dp, 0x7f, sizeof dp); a[0] = a[1] - m; a[n + 1] = INF; for(register int j = 0; j < m; ++j) { dp[0][j] = 0; } int tmp; for(register int i = 1; i <= n; ++i) { for(register int j = 0; j < m && a[i] + j < a[i + 1]; ++j) { for(register int ii = 0; ii < i; ++ii) { tmp = INF; for(register int jj = 0; jj < m && a[ii] + jj < a[ii + 1] && a[ii] + jj + m <= a[i] + j; ++jj) { tmp = min(tmp, dp[ii][jj]); } if(tmp < INF) dp[i][j] = min(dp[i][j], tmp + WAIT(i, j, ii)); } } } for(register int j = 0; j < m; ++j) { ans = min(ans, dp[n][j]); } cout << ans; return 0; }參考程序(70pts)
觀察上面的方程,顯然最容易優化的就是求“$min \left \{dp[ii][jj]+wait(i, j, ii) \right \}$"的部分,我們可以想一下是否可以開一個數組維護這些最小值。
事實上,這是可行的。
由於這個方程中,始終有$0 \leqslant jj$,那我們何不更改$dp[i][j]$的定義為:第$i$個同學在$\left [ a[i], a[i] + j \right ]$的時間段裏出發時,第$1$到第$i$個同學的等待時間之和的最小值$。
這樣,我們只需要每次更新完當前的$dp[i][j]$時,和$dp[i][j-1]$取一個最小值就行了,這樣我們就可以把時間復雜度降到$O(n^{2}m)$,具體的細節可以參考下面給出的程序。
#include <iostream> #include <cstring> #include <algorithm> #define MAX_N (500 + 5) #define MAX_M (100 + 5) #define INF 0x7f7f7f7f #define WAIT(i, j, ii) ((a[(i)] + j) * ((i) - (ii)) - (s[(i)] - s[(ii)])) using namespace std; int n, m; int a[MAX_N], s[MAX_N]; int dp[MAX_N][MAX_M]; int ans = INF; int main() { cin >> n >> m; for(register int i = 1; i <= n; ++i) { cin >> a[i]; } sort(a + 1, a + n + 1); for(register int i = 1; i <= n; ++i) { s[i] = s[i - 1] + a[i]; } memset(dp, 0x7f, sizeof dp); a[0] = a[1] - m; a[n + 1] = INF; for(register int j = 0; j < m; ++j) { dp[0][j] = 0; } int tmp; for(register int i = 1; i <= n; ++i) { for(register int j = 0; j < m; ++j) { if(a[i] + j < a[i + 1]) { for(register int ii = 0; ii < i; ++ii) { tmp = min(m - 1, min(a[ii + 1] - a[ii] - 1, (a[i] + j) - (a[ii] + m))); if(tmp < 0) continue; if(dp[ii][tmp] < INF) dp[i][j] = min(dp[i][j], dp[ii][tmp] + WAIT(i, j, ii)); } } dp[i][j] = min(dp[i][j], dp[i][j - 1]); } } cout << dp[n][m - 1]; return 0; }參考程序
由於我比較菜,有一個$O(nm)$的解法就以後再補充吧。
【題解】擺渡車