日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【NOIP2018普及组】摆渡车

發布時間:2023/12/8 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【NOIP2018普及组】摆渡车 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

@擺渡車@

    • @題目描述@
    • @絕對不可能是正解的題解@
    • @代碼 - 1@
    • @看起來比較像正解的題解@
    • @代碼 - 2@
    • @End@


@題目描述@

有 n 名同學要乘坐擺渡車從人大附中前往人民大學,第 i 位同學在第 ti 分鐘去等車。只有一輛擺渡車在工作,但擺渡車容量可以視為無限大。擺渡車從人大附中出發、把車上的同學送到人民大學、再回到人大附中(去接其他同學),這樣往返一趟總共花費 m 分鐘(同學上下車時間忽略不計)。擺渡車要將所有同學都送到人民大學。
凱凱很好奇,如果他能任意安排擺渡車出發的時間,那么這些同學的等車時間之和最小為多少呢?
注意:擺渡車回到人大附中后可以即刻出發。

輸入
第一行包含兩個正整數 n,m,以一個空格分開,分別代表等車人數和擺渡車往返一趟的時間。
第二行包含 n 個正整數,相鄰兩數之間以一個空格分隔,第 i 個非負整數 ti代表第 i 個同學到達車站的時刻。
輸出
輸出一行,一個整數,表示所有同學等車時間之和的最小值(單位:分鐘)。

輸入樣例 1
5 1
3 4 4 3 5
輸入樣例 2
5 5
11 13 1 5 5

輸出樣例 1
0
輸出樣例 2
4

輸入輸出樣例 1 說明
同學 1 和同學 4 在第 3 分鐘開始等車,等待 0 分鐘,在第 3 分鐘乘坐擺渡車出發。擺渡車在第 4 分鐘回到人大附中。
同學 2 和同學 3 在第 4 分鐘開始等車,等待 0 分鐘,在第 4 分鐘乘坐擺渡車出發。擺渡車在第 5 分鐘回到人大附中。
同學 5 在第 5 分鐘開始等車,等待 0 分。

輸入輸出樣例 2 說明
同學 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 ≤ 10^4。
另有 20% 的數據,n ≤ 500,m ≤ 10,0 ≤ ti ≤ 4×10^6。
對于 100% 的數據,n ≤ 500,m ≤ 100,0 ≤ ti ≤ 4×10^6。

@絕對不可能是正解的題解@

搜到了不少的題解,一個比一個強 orz。
這個算法應該是一個比較容易“想到”的算法(因為題目中很多的性質都沒有用到)
但是有用到斜率優化……
定義 a[i]a[i]a[i] 表示第 i 個時刻到達進行等車的人數。
定義 dp[i]dp[i]dp[i] 表示在第 i 個時刻發車,前面的同學最小等待時間之和。
則有:
dp[i]=min?(dp[j]+(∑k=j+1k≤ia[k]?(i?k)))(i?j>=m)dp[i] = \min(dp[j] + (\sum_{k=j+1}^{k\le i}a[k]*(i-k)))(i-j>=m)dp[i]=min(dp[j]+(k=j+1ki?a[k]?(i?k)))(i?j>=m)
拆開得到:
dp[i]=min?(dp[j]+i?(∑k=j+1k≤ia[k])?(∑k=j+1k≤ia[k]?k))(i?j>=m)dp[i] = \min(dp[j] + i*(\sum_{k=j+1}^{k\le i}a[k])-(\sum_{k=j+1}^{k\le i}a[k]*k))(i-j>=m)dp[i]=min(dp[j]+i?(k=j+1ki?a[k])?(k=j+1ki?a[k]?k))(i?j>=m)
cnt[i]=∑j=1j≤ia[j]cnt[i]=\sum_{j=1}^{j\le i}a[j]cnt[i]=j=1ji?a[j]sum[i]=∑j=1j≤ia[j]?jsum[i]=\sum_{j=1}^{j\le i}a[j]*jsum[i]=j=1ji?a[j]?j。這兩貨可以前綴和O(n)處理。

則轉移方程式變為:
dp[i]=min?(dp[j]+i?(cnt[i]?cnt[j])?(sum[i]?sum[j]))=min?(dp[j]+i?cnt[i]?i?cnt[j]?sum[i]+sum[j])=min?((i?cnt[i]?sum[i])+(dp[j]+sum[j])?i?cnt[j])(i?j>=m)dp[i] = \min(dp[j] + i*(cnt[i]-cnt[j])-(sum[i]-sum[j]))\\=\min(dp[j] + i*cnt[i]-i*cnt[j]-sum[i]+sum[j])\\=\min( (i*cnt[i]-sum[i]) + (dp[j]+sum[j]) - i*cnt[j]) (i-j>=m)dp[i]=min(dp[j]+i?(cnt[i]?cnt[j])?(sum[i]?sum[j]))=min(dp[j]+i?cnt[i]?i?cnt[j]?sum[i]+sum[j])=min((i?cnt[i]?sum[i])+(dp[j]+sum[j])?i?cnt[j])(i?j>=m)
f(i)=(i?cnt[i]?sum[i]),g(j)=(dp[j]+sum[j]),h(j)=cnt[j]f(i)=(i*cnt[i]-sum[i]), g(j)=(dp[j]+sum[j]), h(j)=cnt[j]f(i)=(i?cnt[i]?sum[i]),g(j)=(dp[j]+sum[j]),h(j)=cnt[j]
dp[i]=min?(f(i)+g(j)?i?h(j))(j&lt;=i?m)dp[i]=\min(f(i) + g(j) - i*h(j)) (j&lt;=i-m)dp[i]=min(f(i)+g(j)?i?h(j))(j<=i?m)

典型的不能再典型的斜率優化式…… i 和 h(j) 還具有單調性……

@代碼 - 1@

雖然當初我去都沒去普及組。
不過普及組(涉嫌)考到斜率優化倒是令我極其感興趣,就寫了一下 qwq。

#include<cstdio> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; typedef pair<int, ll> pil; const int INF = (1<<30); const int MAXN = 4000100; int cnt[MAXN + 5]; ll sum[MAXN + 5], dp[MAXN + 5]; ll f(int i) {return cnt[i]*i - sum[i]; } ll g(int j) {return dp[j]+sum[j]; } ll h(int j) {return cnt[j]; } double slope(int x, int y) {if( h(x) == h(y) ) {if( g(x) > g(y) ) return -INF;else if( g(x) < g(y) ) return INF;}else return 1.0*(g(y) - g(x))/(h(y) - h(x)); } int que[MAXN + 5]; int main() {int n, m, x, lim = 0;scanf("%d%d", &n, &m);for(int i=1;i<=n;i++) {scanf("%d", &x);cnt[x]++; lim = max(lim, x);}lim += m;for(int i=1;i<=lim;i++) {sum[i] = sum[i-1] + 1LL*cnt[i]*i;cnt[i] += cnt[i-1];}int s = 1, t = 0;que[++t] = 0;for(int i=1;i<=lim;i++) {if( i > m ) {while( s < t && slope(que[t-1], que[t]) >= slope(que[t], i-m) )t--;if( h(i-m) != h(que[t]) || g(i-m) != g(que[t]) )que[++t] = i-m;}while( s < t && slope(que[s], que[s+1]) <= i )s++;dp[i] = f(i) + g(que[s]) - h(que[s])*i;}ll ans = INF;for(int i=lim-m;i<=lim;i++)ans = min(ans, dp[i]);printf("%lld\n", ans); }

@看起來比較像正解的題解@

考慮到n, m如此之小,我們以它們倆作為狀態進行 dp。

首先有這樣一個比較關鍵的性質:車輛停留的時間不會超過 m ,否則它可以往返一次再回來,這樣不會影響后面的狀態且有可能更優秀。

先按到達時間給人排序。然后定義狀態 dp[i][j]dp[i][j]dp[i][j] 表示第 i 個人等了 j 分鐘,前 i 個人的最小等待時間和。因為上面那個性質,有 j <= 2*m。抵到上界的唯一情況 t[i] = t[i-1],且轉移時于 t[i-1] 時刻發車,回來停留 m 分鐘。

根據狀態定義,可以得到轉移方程:
(1)dp[i][j]=min?(dp[i?1][k]+j)(t[i?1]+k=t[i]+j)(2)dp[i][j]=min?(dp[i?1][k]+j)(t[i?1]+k+m&lt;=t[i]+j)(1)dp[i][j] = \min(dp[i-1][k] + j)(t[i-1]+k=t[i]+j)\\(2)dp[i][j] = \min(dp[i-1][k] + j)( t[i-1] + k + m &lt;= t[i] + j )(1)dp[i][j]=min(dp[i?1][k]+j)(t[i?1]+k=t[i]+j)(2)dp[i][j]=min(dp[i?1][k]+j)(t[i?1]+k+m<=t[i]+j)
對于(1),第 i 個人與第 i-1 個人同車。
對于(2),第 i 個人與第 i-1 個人不同車。
以上都要滿足 0 <= j, k <= 2*m,1<=i<=n。

現在算法時間復雜度為 O(n*m^2) ,雖然已經足以通過此題,但是還可以做到更優秀。
第(1)類轉移是O(1)的,不去管它。
第(2)類轉移,變換條件得到:k <= t[i]+j-t[i-1]-m。可以用前綴最小值快速求解。
時間復雜度O(n*m)。以及代碼復雜度比上面那個斜率優化不知道高到哪里去了。

@代碼 - 2@

考場上同學們好像做得很炸……
不過分析下來其實難點也不算多。只是可能狀態定義不太好想。

#include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int MAXN = 500; const int MAXM = 100; const int INF = (1<<30); int t[MAXN + 5]; int dp[MAXN + 5][MAXM*2 + 5]; int mn[MAXN + 5][MAXM*2 + 5]; int main() {int n, m;scanf("%d%d", &n, &m);for(int i=1;i<=n;i++)scanf("%d", &t[i]);sort(t+1, t+n+1);for(int i=0;i<=2*m;i++) {dp[1][i] = i;mn[1][i] = (i == 0) ? dp[1][i] : min(dp[1][i], mn[1][i-1]);}for(int i=2;i<=n;i++) {for(int j=0;j<=2*m;j++) {dp[i][j] = INF;if( 0 <= t[i] - t[i-1] + j && t[i] - t[i-1] + j <= 2*m )dp[i][j] = min(dp[i][j], dp[i-1][t[i]-t[i-1]+j] + j);if( 0 <= t[i] - t[i-1] + j - m )dp[i][j] = min(dp[i][j], mn[i-1][min(t[i]-t[i-1]+j-m, 2*m)] + j);mn[i][j] = (j == 0) ? dp[i][j] : min(dp[i][j], mn[i][j-1]);}}int ans = INF;for(int i=0;i<=2*m;i++)ans = min(ans, dp[n][i]);printf("%d\n", ans); }

@End@

就是這樣,新的一天里,也請多多關照哦(ノω<。)ノ))☆.。

總結

以上是生活随笔為你收集整理的【NOIP2018普及组】摆渡车的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。