小明最近为了锻炼智力,在玩一个数列求和的游戏。设数列的长度为n,每个 数字都是整数,且在±1000范围内。小明能够从这个数列里面选一串任意长度的连续子串并求和,小明想知道子串和绝对值的最大值是多少,你能帮帮他吗?node
求一个数列里面字段和绝对值的最大值。算法
很显然,这是一道 最大子段和 问题的变种,只是求的是 绝对值。
最大子段和有以下方法:数组
很是 暴力 的方法,很少说。
暴力 枚举 字段 起点、终点 ,对于每一个子串 求和 ,最后求最大绝对值。
求和的过程能够用 前缀和 优化,复杂度 \(O(N^2)\), \(70pt\) 到手。优化
先别急着搞 \(O(N)\) ,实际上分治也很容易想到固然是万物皆可 二分 啦。
若是用二分作法,那么对于每一个区间,最大子段要么就在 左边一半,要么在 右边一半 ,要么 横跨中间。
因此,显然能够这么设计:spa
核心代码:设计
int solve ( int l, int r ) { if ( l == r ) return a [ l ]; int mid = l + ( r - l >> 1 ), sum = 0, ansl = -1e9, ansr = -1e9; for ( int i = mid; i >= l; i -- ) ansl = max ( ansl, ( sum += a [ i ] ) ); sum = 0; for ( int i = mid + 1; i <= r; i ++ ) ansr = max ( ansr, ( sum += a [ i ] ) ); return max ( max ( solve ( l, mid ), solve ( mid + 1, r ) ), ansl + ansr ); }
最大子段和的 \(O(N)\) 作法的确很基础,但对于怎么来的你或许不太清楚,因此这里再提一下。
优化暴力通常分为两种:重复和没必要要。
首先,若是是小于零的负数,那么出现了没必要要的状况,取了还不如不取。
其次,若是画一下图,能够看出不一样起点的差值是固定的,因此只要舍弃 \([i,j]\) ,那么全部 \([*,j]\) 均可以舍弃,直接从 \([j+1,*]\) 开始。这个算法就出来了。code
代码很简单,不写了。队列
在互联网上,会有一些不文明的人发送不文明的言论。咱们的目的,就是要 自动过滤而且识别这些言论 给出一个字符串 S,表示那些可能不文明的言论。再给出一个字典 T,包含了 n 条违规的用语,T[1], T[2], …, T[n]。要在 S 中添加最少的,使得只要违规用语 T[i]在 S 中出现,就得在每一个 T[i]的字符之间添加。 好比说 S=aaabbssss,T[1]=abb,T[2]=bbss。那么最后的符合规定的 S’就为 aaabbssss。其中 abb 在第 3 到第 5 个字符之间出现,bbss 在第 4 到第 7 个字符出现。 数据保证字符串 S 不包括字符‘*’,且 T[i]的长度必定大于 1。 其中|S|≤1000,n≤10,1<|T[i]| ≤100;|S|表示 S 字符串的长度,|T[i]|表示 T[i] 字符串的长度。游戏
把 \(S\) 中全部包含在 \(T\) 中的子段的每一个字符之间添加 ‘*’ ,并输出 S’。字符串
这道题因为 \(|S|≤1000,n≤10,1<|T[i]| ≤100\),能够直接暴力作(而也没有其余方法)。
暴力匹配每一个 \(T[i]\) ,在匹配位置打上标记,输出时在打标记位置输出 ‘*’ 。
核心代码:
for ( int i = 1; i <= n; i ++ ) for ( int j = 1, pos = 1; j <= lens; j ++ ) { if ( t [ i ] [ pos ] == s [ j ] ) pos ++; if ( pos == lent [ i ] ) for ( int k = j; k < j + lent [ i ] - 1; k ++ ) flag [ k ] = true; } //输出 for ( int i = 1; i <= lens; i ++ ) { printf ( "%c", s [ i ] ); if ( flag [ i ] ) putchar ( '*' ); }
给定一个n*m 的网格地图,格子有三种状况:
- ‘.’表示空,能够正常通行
- ‘#’表示有墙,不能通行
- 大写英文字母(A~Z)表示有陷阱,能够通行,但通过会扣必定的血量,并
且不会消失一共有k 个陷阱(编号从A 开始,ABCDE...),k<=26,而且给定起点,终点,
和初始血量H,行走方向只有上下左右四个方向,注意在行走过程当中不能有任意
时刻的血量小于等于0。输出到达终点的最大血量。
额,真的没有
原题中有一个 “30%的样例 k=0”,所以不考虑陷阱,能够那 \(30pt\) 。
DFS很是暴力:
void dfs ( int x, int y ) { vis [ x ] [ y ] = true; if ( x == ex && y == ey ) { ok = true; return ; } for ( int i = 0; i < 4; i ++ ) { tx = x + d [ i ] [ 0 ], ty = y + d [ i ] [ 1 ]; if ( ! vis [ tx ] [ ty ] && tx > 0 && tx <= n && ty > 0 && ty <= m && map [ tx ] [ ty ] != '#' ) dfs ( tx, ty ); } }
输出时若是ok,就输出h,不然就直接输出-1。
虽然仍是 \(30pt\) ,但BFS时间更短,为了后续方便仍是值得写一下。
void bfs ( int sx, int sy ) { que.push ( node { sx, sy } ); while ( ! que.empty ( ) ) { t = que.front ( ); que.pop ( ); if ( t.x == ex && t.y == ey ) { ok = true; return ; } for ( int k = 0; k < 4; k++ ) { tx = t.x + d [ k ] [ 0 ], ty = t.y + d [ k ] [ 1 ]; if ( tx >= 1 && tx <= n && ty >= 1 && ty <= m && map [ tx ] [ ty ] != '#' ) que.push ( node { tx, ty } ); } } }
再在算法1的基础上增长参数h,能够得出一个正确的算法:
void dfs ( int x, int y, int h ) { if ( x == ex && y == ey ) { ans = max ( ans, h ); return ; } for ( int i = 0; i < 4; i ++ ) { tx = x + d [ i ] [ 0 ], ty = y + d [ i ] [ 1 ]; if ( ! vis [ tx ] [ ty ] && tx > 0 && tx <= n && ty > 0 && ty <= m && map [ tx ] [ ty ] != '#' ) vis [ tx ] [ ty ] = true, dfs ( tx, ty ), vis [ tx ] [ ty ] = false; } }
额,TLE & MLE 祝你好运。
你会想,若是要求血量,不就记录到每一个点的扣血,加入队列时计算好了吗:
void bfs ( int sx, int sy ) { que.push ( node { sx, sy } ); while ( ! que.empty ( ) ) { t = que.front ( ); que.pop ( ); if ( t.x == ex && t.y == ey ) return ; for ( int k = 0; k < 4; k++ ) { tx = t.x + d [ k ] [ 0 ], ty = t.y + d [ k ] [ 1 ]; if ( tx >= 1 && tx <= n && ty >= 1 && ty <= m ) que.push ( node { tx, ty } ), dis [ tx ] [ ty ] = dis [ t.x ] [ t.y ] + a /*扣血,请开大一点并预处理(把'.'当作扣血0,把‘#’当作扣血∞)*/ [ map [ tx ] [ ty ] ]; } } }
BUT,这样真的好了吗?
设计一个数据:
S | 1 |
7 | 1 |
E | 1 |
额,你炸了。。。一个点能够有多种方法到达,而最短的那一条不必定扣血最少。这道题和最短路径没有关系。
那咋办呢?
实际上,求最短路径有一个 “松弛” 算法:
if ( dis [ u ] > dis [ v ] + dam [ u ] ) dis [ u ] = dis [ v ] + dam [ u ];
不难看出,“松弛” 就是经过介入第三点来缩短路径,其实就是一个贪心。所以,有两种 “松弛” 方式:
void dfs ( int x, int y, int h ) { for ( int i = 0; i < 4; i ++ ) { tx = x + d [ i ] [ 0 ], ty = y + d [ i ] [ 1 ]; if ( ! vis [ tx ] [ ty ] && tx > 0 && tx <= n && ty > 0 && ty <= m && map [ tx ] [ ty ] != '#' ) if ( dis [ tx ] [ ty ] > dis [ x ] [ y ] + a [ map [ tx ] [ ty ] ] ) dis [ tx ] [ ty ] = dis [ x ] [ y ] + a [ map [ tx ] [ ty ] ], dfs ( tx, ty ); } }
void bfs ( int sx, int sy ) { que.push ( node { sx, sy } ); while ( ! que.empty ( ) ) { t = que.front ( ); que.pop ( ); for ( int k = 0; k < 4; k++ ) { tx = t.x + d [ k ] [ 0 ], ty = t.y + d [ k ] [ 1 ]; if ( tx >= 1 && tx <= n && ty >= 1 && ty <= m ) if ( dis [ tx ] [ ty ] > dis [ x ] [ y ] + a [ map [ tx ] [ ty ] ] ) que.push ( node { tx, ty } ), dis [ tx ] [ ty ] = dis [ t.x ] [ t.y ] + a [ map [ tx ] [ ty ] ]; } } }
在这个BFS中,其实每一个点都只用入队/出队一次,数值就固定了,之后不须要更新。
所以,能够再把FLAG数组开回来,每一个点只要入队一次就打上标记,之后就不用再入队了:
void bfs ( int sx, int sy ) { que.push ( node { sx, sy } ); flag [ sx ] [ sy ] = true; while ( ! que.empty ( ) ) { t = que.front ( ); que.pop ( ); flag [ t.x ] [ t.y ] = false; for ( int k = 0; k < 4; k++ ) { tx = t.x + d [ k ] [ 0 ], ty = t.y + d [ k ] [ 1 ]; if ( tx >= 1 && tx <= n && ty >= 1 && ty <= m ) if ( dis [ tx ] [ ty ] > dis [ x ] [ y ] + a [ map [ tx ] [ ty ] ] ) { if ( ! flag [ tx ] [ ty ] ) que.push ( node { tx, ty } ), flag [ tx ] [ ty ] = true; dis [ tx ] [ ty ] = dis [ t.x ] [ t.y ] + a [ map [ tx ] [ ty ] ]; } } } }
嗯,恭喜你,你一不当心发明了SPFA ( Shortest Path Fast Algorithm )。
其实对于 “松弛” 操做,还有另外一种更暴力的方法:不断把矩阵里每个点进行 “松弛”,直到所有完毕,没有再更新的为止。
void Bellman_Ford ( int sx, int sy ) { memset ( dis, 0x3f, sizeof ( dis ) ); dis [ sx ] [ sy ] = 0; for ( bool flag = true; ! flag; flag = true ) { for ( int x = 1; x <= n; x ++ ) for ( int y = 1; y <= m; y ++ ) for ( int k = 0; k < 4; k++ ) { tx = x + d [ k ] [ 0 ], ty = y + d [ k ] [ 1 ]; if ( tx > 0 && tx <= n && ty > 0 && ty <= m && map [ tx ] [ ty ] != '#' ) if ( dis [ tx ] [ ty ] > dis [ x ] [ y ] + a [ map [ tx ] [ ty ] ] ) dis [ tx ] [ ty ] = dis [ x ] [ y ] + a [ map [ tx ] [ ty ] ], flag = false; } }
你可能很快就看出来了贝尔曼-福特算法有着一个致命的缺陷:每一轮都遍历了不少无用的点,因此能够每一轮都只把伤害最少的一个点周围的四个点 “松弛” 一下,这样每一个点至多更新一次,快乐(误 快了不少。另外,Dijkstra算法还能够作一个队列优化,基本上就变成了SPFA。
void Dijkstra ( int sx, int sy ) { memset ( dis, 0x3f, sizeof ( dis ) ); dis [ sx ] [ sy ] = 0; for ( int cnt = 1; cnt <= n* m; cnt ++ ) { int mindis = 0x3f3f3f3f, minx, miny; for ( int x = 1; x <= n; x ++ ) for ( int y = 1; y <= m; y ++) if ( ! flag [ x ] [ y ] && dis [ x ] [ y ] < mindis ) mindis = dis [ x ] [ y ], midx = x, miny = y; if ( mindis == 0x3f3f3f3f ) break; flag [ minx ] [ miny ] = true; for ( int k = 0; k < 4; k ++ ) { int tx = minx + d [ k ] [ 0 ], ty = miny + d [ k ] [ 1 ]; if ( tx >= 1 && tx <= n && ty >= 1 && ty <= m ) if ( dis [ tx ] [ ty ] > dis [ x ] [ y ] + a [ map [ tx ] [ ty ] ] ) dis [ tx ] [ ty ] = dis [ t.x ] [ t.y ] + a [ map [ tx ] [ ty ] ]; } } }
额,还没写好