前言
轉眼已經到5月,可是我還沒訂17年的計劃,真是悲傷的故事。
今年還是想花點時間,玩玩題目。
題目不會太簡單,也不會太難,簡單的如1、2題,難的如3、4題。
*Never enough time, neither the thing. *
正文
1、Efim and Strange Grade
題目鏈接
題目大意:
輸入一個長度為n的字符串表示一個浮點數;
每次可以對浮點部分進行一次四舍五入,10.45 => 10.5;
最多可以進行t次,輸出t次進位后,最大的數。
n and t (1?≤?n?≤?200?000, 1?≤?t?≤?1e9)
** Examples**
** input**
6 2
10.245
** output**
10.3
樣例解釋:10.245第一次四舍五入變成10.25,再次四舍五入變成10.3;
** 題目解析:**
首先可以確定,如果小數第一位x[1]>=5,那么直接進位即可;
x[1] < 4, 肯定不會進位,因為即使后面進位上來最多x[1]+1=4,達不到進位的條件;
x[1] == 4的情況,如果x[1 + 1] >= 5,則可能發生x[1+1]進位后導致x[1]進位;
那么對于第i位:
x[i]>=5的時候,第i位之后的數字無意義;
x[i]==4的時候,有可能從后面傳遞過來;
x[i]<4的時候,進位停止;
最后在考慮一種特殊情況:進位導致進位的情況,比如說99.9949進位2次,會變成100.00。
2、Destroying Array
題目鏈接
題目大意:給出n個數字a[i]的數組。(1?≤?a[i]?≤?1e9)
給出一個位置p[i](1<=p[i]<=n),把p[i]對應的數字destroy,數字所在的數組分為兩部分。
現在給出n個位置(不重復),輸出每次destroy后,最大的數組段數字和。
(1<=n<=10w)
Example
** input**
4
1 3 2 5
3 4 1 2
** output**
5
4
3
0
樣例解釋:1325的數組,在destroy第三個數字后,變成13和5,最大的值為5;
** 題目解析:**
每次destroy的操作會去掉一個數字,同時產生兩個數字;
先看看暴力的做法:
每次從位置p[i]開始,計算和p[i]在同個數組的左邊數組之和sumLeft,右邊數組之和sumRight,再把sumLeft和sumRight與其他數組和進行比較;
時間的復雜度是O(N),總得復雜度是O(N^2);
優化方案: 反著計算!
f[i] 表示 數字i所在序列最左邊的數字,sum[i]表示第i個數字所在序列的數字和。
反著來看這個操作,每次插入一個數字,合并數字所在左右區間,然后詢問最大的區間和。(并查集)
3、Generating Sets
題目鏈接
** 題目大意:**給出一個set,是setY,由n個不同的正整數y[i]組成;
setX由n個不同的正整數x[i]組成,現在可以對任意x[i]進行多個以下操作:
1、x[i] = 2 * x[i];
2、x[i] = 2 * x[i] + 1;
如果經過操作后的setX和setY是相同的,認為setX能生成setY。(按照從大到小的排序后,一一對應)
現在給出n個數字y[i],求出set X,要求setX的最大數字最小;(如果有多個答案,輸出任意一個)
(1?≤?y[i]?≤?1e9)
(n = 5w)
** Examples**
input
5
1 2 3 4 5
output
4 5 2 3 1
** 題目解析:**
題目給出的操作意思是:如果x的二進制表示,是y的前綴(y的二進制表示的左邊部分),那么x可以轉換成y。
現在給出y[i],在題目的要求中,必然存在一個解,就是x[i]=y[i];
容易看出,最大的數字最小這個限制滿足二分。
現在的問題是,如何迅速判斷,在最大的數字不超過mid的情況下,是否存在合適的解?
這里需要用到一個貪心的性質,如果a>b,那么優先匹配a,并且要在a<=mid的情況下,a匹配的數字盡可能大;
于是可以把數組y[i]排序,從最大的開始匹配,如果y[i]>mid,則y[i] >>= 1,直到出現一個合法的解,添加標記;
如果一個數字y[i]變到0,則無解,因為題目要求是正整數;
每次求解的過程為O(NlogM),總得的復雜度是O(N*logM*logM),M是數字y[i]的范圍;
4、Research Rover
題目鏈接
** 題目大意:**
一個網格,總共有n*m的cell,cell可以上下左右走,一個人帶著一個電量為x的電池,從(1,1)出發到(n,m),隨機選擇一條最短路徑;
有k個特殊的cell,經過這個cell時,電池的電量會減半;(向上取整)
輸入n、m、k和k個cell的位置,求到達終點時,電池電量的期望值。(P/Q mod 1e9+7)。
(1?≤?n,?m?≤?100?000, 0?≤?k?≤?2000, 1?≤?x?≤?1?000?000)
Example
** input**
3 3 2 11
2 1
2 3
** output**
333333342
** 題目解析:**
根據題目的限制--電池的電量會減半,我們知道:
最多有logS個選擇。
那么,把障礙點排序(起點和終點也要加入)
f[i][j] 表示前j個,經過i個障礙的可能數;
g[i][j] 表示前j個,至少經過i個障礙物的可能數;
那么有
g[i][j]=∑f[i?1][k]?Ways[k][j]
f[i][j]=g[i][j]?∑f[i][k]?Ways[k][j]
Ways[i][j]表示i到j的方案數
這里需要用到組合數公式:
C(n,m) = A(n,m)/A(n,n)=m! / ((m-n)! * n!) = m! * (m-n)!^(-1) * n!^(-1);
還需要用到乘法逆元的知識:逆元詳解
5、Road to Home
題目鏈接
** 題目大意:**
在一條數軸上,Bob要學校(點0)走到家(點L),其中有n個路燈,路燈照耀的范圍是(l[i], r[i])(路燈的范圍不會重疊);
Bob會在燈亮的的范圍內唱歌,每次唱的距離為p;(必須唱完,中間必須全部是在路燈照耀范圍內)
當Bob唱完一次的時候,下一次唱的地點必須滿足以下其中一點:
1、開始唱的地點和上一次唱結束的地點重合;
2、開始唱的地點和上一次唱結束的地點距離>=t;
問最多,能唱幾次。
(1?≤?L?≤?1e9, 0?≤?n?≤?100?000, 1?≤?p?≤?1e9, 1?≤?t?≤?1e9)
Examples
** input**
17 2 2 6
0 9
13 17
output
5
** 題目解析:**
先看清題目條件的重點:路燈(l[i], r[i])不會重疊,并且唱歌必須在路燈照耀范圍內,兩次唱歌的距離間隔要么為0,要么>=t;
那么對于bob來說,每到一個路燈的范圍內,他需要作出的是否唱歌的抉擇:
1、唱歌。需要看當前是否滿足唱歌的條件:剩下的路燈距離是否足夠p,當前位置和上次唱歌的位置是否滿足唱歌的距離條件;
2、不唱歌。對前面沒有要求。
那么這里就有一個典型的最優子結構:
假設dp[i]為到距離i能唱的最多次數(并且要求是以唱歌結尾),且區間[i-p, i]是在路燈范圍內,那么有:
dp[i] 可以由 dp[i-p]轉移過來;
dp[i] 可以由 max(dp[0 ~ (i-t-p)]) + 1;
這樣的狀態數為L,L是距離的范圍,狀態轉移的復雜度同樣為O(L);
再回歸到題目的數據范圍,進行數據優化:
其中的max(dp[0 ~ (i-t-p)]),可以采取dmax[i]來表示前i個位置的最優解,這樣max(dp[0 ~ (i-t-p)])就等于dmax[i-t-p];
這樣狀態轉移的復雜度就將為O(1),但是狀態數仍有O(L),而L非常大;
考慮到路燈的區間性,dp[i]改為到距離r[i]能唱的最多次數,需要注意的是,這里不能要求到r[i]以唱歌結尾,因為以唱歌結尾不滿足最優解,比如說(1, 3)的區間,唱歌p=2,此時最優解應該是1次,距離為(1, 2),而不是唱歌結尾的(2, 3);
我們引入一個新的變量g[i],表示dp[i]取到最優解的最左邊距離;
這樣在狀態轉移的時候,假設是從dp[k]轉移到dp[i],那么從g[k]開始,連續t的距離不唱歌,然后剩下min(r[i]-l[i], r[i]-g[k]-t)的距離用于唱歌,這里我們用count(L)表示距離L能唱的次數,最后得到dp[i]取最優解的時候g[i]=max(l[i], g[k]+t) + count(L)p*;
對于所有g[k] + t <= r[i]的k,都可以進行狀態轉移:
dp[i] = max(dp[i], dp[k] + count(L));
g[i] = g[k]+count(L)*p+t, L=min(r[i]-l[i], r[i]-g[k]-t)
這樣的狀態數為O(N), 轉移的復雜度為O(N),總的復雜度仍為O(N^2);
仍然需要進行優化,觀察轉移的過程,對于dp[k]是固定值,count(L)中的L會隨著i的增加而增加,而當L很大之后,dp[k]較小的狀態再進行最優化轉移是無效的過程,因為dp[k+1]等會是更合適的解;
狀態的決策因素有兩個,dp[k]和g[k];
對于dp[k]小,g[k]小的解,不能舍棄,因為g[k]小存在轉移的可能;
對于dp[k]大,g[k]大的解,不能舍棄,因為dp[k]大存在轉移的可能;
對于dp[k]小,g[k]大的解,舍棄;
對于dp[k]大,g[k]小的解,不能舍棄,因為g[k]小和dp[k]大均存在轉移的可能;
以上這四種情況,就是dp[k]+count(L)在轉移可能遇到的情況;
count(L)函數是關于L單調遞增的函數;
因為對于i < j, 有 r[i] < l[j], 必然有 g[i] <= g[j],對于i<j, 有count(L[i]) < count(L[j]);
可以看出dp[k]+count(L)是具有決策單調性 ,同時每個決策有一個有效區間r[i]-g[k]-t>=0開始;
那么維護一個決策的隊列,根據dp[k]+count(L)可以算出當前所有的有效決策中的最優解;
并且隨著r[i]的的增加,從r[i]-g[k]-t>=的隊列備選方案中,不斷更新決策隊列的dp[k]+count(L);
詳見代碼:
deque<int> q;
q.push_back(0);
g[0] = -t;
int ans = 0;
for (int i = 1; i <= n; ++i) {
int k = -1;
while (!q.empty() && g[q.front()] + t <= r[i]) { //每次檢測是否進入下一個值的有效區間
int nl = max(l[i], g[q.front()] + t), nr = r[i];
if (dp[q.front()] + (nr - nl) / p > dp[i]) {
dp[i] = dp[q.front()] + (nr - nl) / p;
g[i] = nl + (nr - nl) / p * p;
}
else if (dp[q.front()] + (nr - nl) / p == dp[i]) {
g[i] = min(g[i], nl + (nr - nl) / p * p); // 同樣的dp值,只保留最小的g[i]值
}
k = q.front();
q.pop_front();
}
if (k != -1) {
q.push_front(k);
}
if (dp[i] > ans) {
ans = dp[i];
q.push_back(i);
}
ans = max(ans, dp[i]);
}
cout << ans << endl;
總結
這幾道題比較難,需要花費點時間進行思考。
時間永遠是不夠的,人生是那么短暫,盡量做自己覺得開心的事情。
做任何事,少帶一些利益之心,過程就是最大的收獲。