程序员进阶之算法练习(二十一)

前言

转眼已经到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;

总结

这几道题比较难,需要花费点时间进行思考。
时间永远是不够的,人生是那么短暂,尽量做自己觉得开心的事情。
做任何事,少带一些利益之心,过程就是最大的收获。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容