解答:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 关键是把题目转化成:
// 同时两个人从(0,0) -> (n-1,n-1)
// 另外,行数和列数定义后,可以走的次数是固定。例如,现在有r行, l列,那么最多可以走的次数是r+l-1
//
// 记行数为r,列数为l,当前走的步数为t,1<=t<=r+l-1
//
// 观察到,一个人现在的位置是(x, y),已经走了t步,那么x+y=t+1。因此,两个人移动的时候可以记录状态
// c[t][x1][x2]表示两个人移动了t步,同时的位置分别是(x1, -tx1+1),(x2, -tx2+1)捡到的最多的樱桃数
//
// 状态转移函数:
// 1. c[t][x1][x2] = -1 if grid[x1][t-x1+1] == -1 || grid[x2][t-x2+1] == -1
// 任何一个人所在的位置是无法通过的,那么这个状态下的樱桃数就是不可能存在的,用-1标记
//
// 2. c[t][x1][x2] = grid[x1][t-x1+1] + (x1 == x2 ? 0 : grid[x2][t-x2+1]) +
// max(c[t-1][x1][x2],c[t-1][x1-1][x2],c[t-1][x1][x2-1],c[t-1][x1-1][x2-1])
// grid[x1][t-x1+1] 表示第一个人所在的位置樱桃的数量
// x1 == x2 判断的目的是避免重复计算,因为这个时候两个人站在同一个位置上面
// c[t-1][x1][x2] 两个人都往下走
// c[t-1][x1-1][x2] 第一个人往右走,第二个人往下走
// c[t-1][x1][x2-1] 第一个人往下走,第二个人往右走
// c[t-1][x1-1][x2-1] 两个人都往右走
int cherryPickup(int** grid, int gridRowSize, int gridColSize) {
int c[2][51][51], cc, n, p, mi, mj, m, i, j;
int t = 1, l = gridRowSize + gridColSize, xi, yi, xj, yj;
for (i = 0; i <= gridRowSize; i++) {
for (j = 0; j <= gridColSize; j++) {
c[0][i][j] = -1;
c[1][i][j] = -1;
}
}
c[(t-1)&1][1][1] = grid[0][0] == -1 ? -1: 0; // for simplify the logic
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
for (; t < l; t++) {
for (xi = 1, mi = min(gridRowSize + 1, t + 1); xi < mi; xi++) {
yi = t - xi + 1;
if (yi > gridColSize) continue;
i = grid[xi-1][yi-1];
for (xj = 1, mj = min(gridColSize + 1, t + 1); xj < mj; xj++) {
yj = t - xj + 1;
if (yj > gridColSize) continue;
j = grid[xj-1][yj-1];
n = t & 1;
if (i == -1 || j == -1) {
c[n][xi][xj] = -1;
continue;
}
cc = i;
if (xi != xj) {
cc += j;
}
cc += m;
c[n][xi][xj] = cc;
}
}
}
m = max(c[(t-1)&1][gridRowSize][gridColSize], 0);
#undef max
#undef min
return m;
}
文章:https://medium.com/@rdsubhas/10-modern-software-engineering-mistakes-bc67fbef4fc8
例举10种软件过度工程化的问题。
- 过度预期业务需求。事实是业务的变化总为超乎你的估计,因此专注眼前的设计。
- 重用业务功能。事实是因为业务逻辑的易变性,使得重用部分变得越来越臃肿,本质是业务逻辑之间的耦合,解耦之后,公用部分就会变成薄薄的一层。
- 过度通用化(抽象化)。事实是当你刚刚抽象完以后,需求又变了,过度抽象也是有问题的。
- 浅封装。封装一切外部库。事实是很多库质量已经很高,另外封装和库紧密结合,库的修改一样会导致代码修改,有时候封装倒是多余。
- 盲目使用代码质量规则。事实是10行完成的
hello,world
,通过使用SOILD花了100多行代码。需要了解每个原则解决的问题。 - 为技术而技术。泛型很酷,来个
HelloWorldPrinter<String, Writer>
;策略模式很巧,见到if
就用它来替换。 - 过度最求可配置性、安全性、扩展性、可维护性。单从一个点上看都没毛病,关键是它解决了你的问题了吗,如果跟你的问题关系不大,那就是把力气花错地方了。
- 闭门造车。一个好用的库需要深刻理解业务领域知识,同时需要花费时间去维护。所以,在使用开源方案和自己开发一个之间需要做权衡。
- 对老代码保持沉默。坚持重构,没有代码是不可以改的,目的是为了保持代码代码干净,可维护,可扩展。(稳定的代码谁愿意改哦)
- 高估自己的能力。软件高质量除了技能,还需要时间。事实是能力很强的团队搞出了不怎么的玩意。
软件工程,问题导向,一切都是为了需要解决的问题;学的越多,感觉越没有一种固定的方法,一切都是你能hold住为前提。
less命令中,通过xxg
跳转到xx
行,如果没有xx
默认跳到第一行,对应的还有G
,除了默认跳到最后一行,其他和g
一样。
又名ISP(Interface Segregation Principles)。使用者不应该依赖它不使用的方法。所以,分离的使用者意味着分离的接口。
当你依赖的接口包含不需要的方法时,加上依赖的传递性,从源代码角度看当接口的改变,你的代码可能会跟着改变(这是因为对动态语言来说不用修改原来的代码),从架构角度看由于组件依赖,当组件修改时,会导致组件的重新编译、发布。ISP的目的还是减少类、模块间的耦合,提供类、模块的内聚性,提高代码的可扩展性、可复用性。
有两类接口:
- class interface,在类层面履行接口,每个实现细节实现具体的接口,在golang中就是
interface
定义的接口。 - object interface,在对象层面履行接口,每个新建的对象拥有类的方法,在golang中就是
struct
定义的方法。
SRP也强调职责分离,虽然它的效果也会有ISP的效果,但是它是从业务逻辑的角度去归类职责并进行分离。而ISP是从接口的角度去分离接口,它是在SIP的基础上进一步的细分。
几个比较好的实践:
- 一个接口是只服务于一个模块或者业务逻辑。
- 尽量减少公共的方法。
- 保持接口的干净。如果有污染尽快修复。