CCF201809-1 卖菜

问题描述
  在一条街上有n个卖菜的商店,按1至n的顺序排成一排,这些商店都卖一种蔬菜。
  第一天,每个商店都自己定了一个价格。店主们希望自己的菜价和其他商店的一致,第二天,每一家商店都会根据他自己和相邻商店的价格调整自己的价格。具体的,每家商店都会将第二天的菜价设置为自己和相邻商店第一天菜价的平均值(用去尾法取整)。
  注意,编号为1的商店只有一个相邻的商店2,编号为n的商店只有一个相邻的商店n-1,其他编号为i的商店有两个相邻的商店i-1和i+1。
  给定第一天各个商店的菜价,请计算第二天每个商店的菜价。
输入格式
  输入的第一行包含一个整数n,表示商店的数量。
  第二行包含n个整数,依次表示每个商店第一天的菜价。
输出格式
  输出一行,包含n个正整数,依次表示每个商店第二天的菜价。
样例输入
8
4 1 3 1 6 5 17 9
样例输出
2 2 1 3 4 9 10 13
数据规模和约定
  对于所有评测用例,2 ≤ n ≤ 1000,第一天每个商店的菜价为不超过10000的正整数。

解题要点

不多说,熟悉语言就没有问题,简单到炸

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <vector>
using namespace std;

int main()
{
int n; cin >> n;
vector<int> p(n);
for (int i = 0; i < n; i++) {
cin >> p[i];
}
vector<int> np(n);
np[0] = (p[0] + p[1]) / 2;
for (int i = 1; i < n-1; i++) {
np[i] = (p[i - 1] + p[i] + p[i + 1])/3;
}
np[n-1] = (p[n-2] + p[n-1]) / 2;
for (int i = 0; i < n; i++) {
cout << np[i] << " ";
}
return 0;

}

leetcode-整数翻转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func reverse(x int) int {
const (
intMax = 2147483647
intMin = -2147483648
)
result := 0
for x != 0 {
t := x % 10
if result > intMax/10 || (result==intMax/10 && t>intMax%10) { return 0 }
if result < intMin/10 || (result==intMin/10 && t<intMin%10) { return 0 }
result = result*10 + t
x /= 10
}
return result
}

刚开始使用了一开始就计算出最高位权值,进而直接乘上到达目的位的方法。
此种方法首先要事先求出数字位数才能求各位权值,进行一次循环浪费了时间,
求权值过程中又要进行一次循环,增加时间又增加空间,后续也没有太好的方法判断溢出。
类似

1
2
3
4
5
6
7
8
9
10
11
12
numLen := getNumLen(x)
w:=1
for i:=0; i<numLen;i++ {
w*=10
}
r := 0
for x!=0 {
t := x % 10
r += w * t

w /= 10; x /= 10
}

后来看了循环乘10只需要一次循环,还省去了许多过程与变量,又高效又优雅

1
2
3
4
5
6
7
r := 0
for x!=0 {
t := x %10
x /= 10

r = r * 10 + t
}

而后是添加判断溢出的方法,需要使用数学推导。

重点在于r * 10 + t这一句需要提前判断是否会导致溢出。

假设x为正数,则溢出时有10r+t>intMax。
但是我们不能直接这么写if,因为这样判断本身也会溢出,无法正确判断。
将等式同/10,此时便可以安全判断,r+t/10>intMax/10
但是注意又有新的问题,int会取整,导致/10后范围并不正确。

t的范围是0-9的整数,/10后为[0, 1),取整后得0.
intMax/10=214748364+0.7小数部分是原个位数字,取整后丢失小数,得214748364
若r>[intMax/10],就有r>intMax/10+x(x的范围[0,1)),由于x与t范围[0, 1)一致,也有r>intMax/10+t成立,溢出。
若r==[intMax/10],则还需判断t/10和丢失的小数部分0.7的关系,才能判断原式是否成立,也即t和原intMax的个位数字7的关系,若t>7则成立,溢出。

类似的,负数有
若r<[intMin/10],就有r>intMin/10+x(x的范围(-1, 0]),由于x与t范围(-1, 0]一致,也有r>intMax/10+t成立,溢出。
若r==[intMin/10],则还需判断t/10和丢失的小数部分-0.8的关系,才能判断原式是否成立,也即t和原intMax的个位数字-8的关系,若t<-8则成立,溢出。

总结:

  • 重视数学推导,思路不清晰时可使用数学方法。
  • 计算机的类型虽然有上限,但是数学推导的时候没有范围限制。因而,可以使用数学推导方法推导条件,然后通过变形式子,使得各中间值都落入计算机的表示范围内,如此绕过数值范围问题。
  • 处理此类数值的各位数字时,不必一开始就计算出各位的权值而一次性乘上,也可以通过循环叠加*10来达到目的。

CCF2018-12-3 CIDR合并

题目描述

题目主干

题目描述1
题目描述2
题目描述3

测试用例

样例输入
2
1
2
样例输出
1.0.0.0/8
2.0.0.0/8

样例输入
2
10/9
10.128/9
样例输出

10.0.0.0/8

样例输入
2
0/1
128/1
样例输出
0.0.0.0/0

评测用例规模与约定

评测用例规模与约定1
评测用例规模与约定2

解题要点

  1. 定义存储IP前缀的结构体,并在输入时完成标准化,有助于简化后续处理. 输出时则只需要输出标准格式.

  2. 排序时,由于STL list不是随机访问容器,不能使用<algorithm>库的sort(RandomIterator begin, RandomIterator end, Compare pred)
    但STL list容器自身提供了排序方法list.sort(Compare pred)

同时,由于是自定义结构,需要自行编写STL Compare,可以使用lambda表达式,也可以使用普通函数。
注意如何编写合法的STL Compare(需满足严格弱序、返回值代表第一个参数是否比第二个参数先出现)。

  1. 位运算相关知识。
    还可能涉及整型提升(Integer Promotion)。

  2. 对题目提示阅读理解及实现
    第二步从小到大合并,即为消除被其他前缀包含的子集前缀过程的过程。
    只要按较短的一个前缀的长度len,比较两个IP前缀的前len位是否相同,相同则代表长度较长的那个是长度较短的子集。
    由于排序保证后者范围更小,因此后者是子集,保留前者,删去后者。
    (此过程中也可以两前缀的长度相同,如果前len位相同,则代表两个前缀等价。
    由于排序保证后者地址更大,因此后者地址段包含无效数据更多,也是保留前者,删去后者)

第三步同级合并,即为将两个相同长度的前缀,合并为一个更短的前缀。
只要判断两个前缀的最后一个有效位不同,且前面len-1位相同,则可以两者合并为一个len-1长度的前缀。
可以用异或运算。

以上两步都要注意,由于在循环遍历中修改了遍历对象,无论对迭代器做前进还是后退更改,都要再次考虑迭代器的有效性、是否破坏了循环过程。

  1. 由于简化过程中需要大量修改容器内容,不要使用顺序容器vector等储存,
    否则修改时造成大量元素复制时间,后几个大规模测试数据中极易超时。
    推荐使用STL的list存储。

输入时,也可以预先申请空间而不是每次输入都调用push。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include <cstdio>
#include <iostream>
#include <list>
#include <string>
#include <algorithm>
using namespace std;

using IPAddr = unsigned int;
using Byte = unsigned char;
struct IPPrefix {
Byte ip[4];
short len;
bool isSubsetOf(const IPPrefix& o) {
if (this->len < o.len) {
return false;
}
auto lessLen = o.len;
IPAddr mask = 0;
for (int i = 0; i < lessLen; i++) {
mask |= 1U << (32 - (i+1));
}
IPAddr ip1 = this->getIP();
IPAddr ip2 = o.getIP();
IPAddr ip1subnet = ip1 & mask;
IPAddr ip2subnet = ip2 & mask;
return ip1subnet == ip2subnet;
}
inline IPAddr getIP() const {
return ip[0] << 24
| ip[1] << 16
| ip[2] << 8
| ip[3];
}
bool operator==(IPPrefix& rhs) const {
return this->getIP() == rhs.getIP()
&& this->len == rhs.len;
}
};

inline istream& operator>>(istream& is, IPPrefix& rhs) {
string input; is >> input;
string::size_type si = input.find_first_of('/');

string ipStr = input.substr(0, si);
string::size_type preDot = -1;
string::size_type nextDot = ipStr.find_first_of('.');
rhs.ip[0] = stoi(ipStr.substr(0, nextDot));
preDot = nextDot;
int sectionCnt = 1;
for (int i = 1; i < 4; i++) {
if (preDot == string::npos) {
rhs.ip[i] = 0;
}
else {
nextDot = ipStr.find_first_of('.', preDot + 1);
sectionCnt++;
string ipSection = ipStr.substr(preDot + 1, nextDot - (preDot + 1));
rhs.ip[i] = stoi(ipSection);
preDot = nextDot;
}
}

if (si == string::npos) {
rhs.len = 8 * sectionCnt;
}
else {
rhs.len = stoi(input.substr(si + 1));
}
return is;
}

inline ostream& operator<<(ostream& os, const IPPrefix& rhs) {
printf("%d.%d.%d.%d/%d", rhs.ip[0], rhs.ip[1], rhs.ip[2], rhs.ip[3], rhs.len);
return os;
}

inline void optimizeList(list<IPPrefix>& data) {
data.sort([](const IPPrefix& lhs, const IPPrefix& rhs) {
IPAddr ip1 = lhs.getIP();
IPAddr ip2 = rhs.getIP();
return ip1 < ip2
||
(ip1 == ip2 && lhs.len < rhs.len);
});
// 消除子集
for (auto i1 = data.begin(), i2 = ++data.begin(); i2 != data.end(); i1++, i2++) {
//clog << "compare1 " << *i1 << " & " << *i2 << endl;
while (i2!=data.end() && i2->isSubsetOf(*i1)) {
// clog << *i2 << " is subset of " << *i1 << ", erase former"<< endl;
i2 = data.erase(i2);
}
if (i2 == data.end()) {
break;
}
//clog << "compare1 " << *i1 << " & " << *i2 << endl;
}
// 合并同级
auto i1 = data.begin(), i2 = ++data.begin();
while (i2 != data.end()) {
if (i1->len == i2->len) {
IPAddr ip1 = i1->getIP();
IPAddr ip2 = i2->getIP();
auto len = i1->len;
// clog << "compare2 " << *i1 << " & " << *i2 << endl;
if ((ip1 ^ ip2) >> (32 - len) == 1U) // 前len-1位相同且第len位不同
{
// 合并
// clog << "merge " << *i1 << " &" << *i2 << endl;
i1->len--;
i2 = data.erase(i2);
// clog << "into " << *i1 << endl;
// 回退以检查合并产生的条目,与前一条目是否可以再次合并
if (i1 != data.begin()) {
// 若i1指向begin,则没有前一条,无需回退。
i1--;
i2--;
// 此处continue跳过了本次循环后面的自增操作。
continue;
}
else if (i2 == data.end()) {
// 由于减少一条目,此时i2可能已经到达表尾。
// 立即退出循环,否则再次i2自增会出错
break;
}
}
}
// 前进
i1++; i2++;
}
}

int main()
{
size_t n; cin >> n;
list<IPPrefix> list(n);
for (auto it = list.begin(); it != list.end(); it++) {
cin >> *it;
}
optimizeList(list);
for (auto it = list.cbegin(); it != list.cend(); it++) {
cout << *it << endl;
}
}

CCF2018-12-2 小明放学

题目描述

题目背景

  汉东省政法大学附属中学所在的光明区最近实施了名为“智慧光明”的智慧城市项目。具体到交通领域,通过“智慧光明”终端,可以看到光明区所有红绿灯此时此刻的状态。小明的学校也安装了“智慧光明”终端,小明想利用这个终端给出的信息,估算自己放学回到家的时间。

问题描述

  一次放学的时候,小明已经规划好了自己回家的路线,并且能够预测经过各个路段的时间。同时,小明通过学校里安装的“智慧光明”终端,看到了出发时刻路上经过的所有红绿灯的指示状态。请帮忙计算小明此次回家所需要的时间。

输入格式

  输入的第一行包含空格分隔的三个正整数 r、y、g,表示红绿灯的设置。这三个数均不超过 10^6。
  输入的第二行包含一个正整数 n,表示小明总共经过的道路段数和路过的红绿灯数目。
  接下来的 n 行,每行包含空格分隔的两个整数 k、t。k=0 表示经过了一段道路,将会耗时 t 秒,此处 t 不超过 10^6;k=1、2、3 时,分别表示出发时刻,此处的红绿灯状态是红灯、黄灯、绿灯,且倒计时显示牌上显示的数字是 t,此处 t 分别不会超过 r、y、g。

输出格式

  输出一个数字,表示此次小明放学回家所用的时间。

测试用例

样例输入

1
2
3
4
5
6
7
8
9
10
30 3 30
8
0 10
1 5
0 11
2 2
0 6
0 3
3 10
0 3

样例输出

1
46

样例说明
小明先经过第一段路,用时 10 秒。第一盏红绿灯出发时是红灯,还剩 5 秒;小明到达路口时,这个红绿灯已经变为绿灯,不用等待直接通过。接下来经过第二段路,用时 11 秒。第二盏红绿灯出发时是黄灯,还剩两秒;小明到达路口时,这个红绿灯已经变为红灯,还剩 11 秒。接下来经过第三、第四段路,用时 9 秒。第三盏红绿灯出发时是绿灯,还剩 10 秒;小明到达路口时,这个红绿灯已经变为红灯,还剩两秒。接下来经过最后一段路,用时 3 秒。共计 10+11+11+9+2+3 = 46 秒。

评测用例规模与约定

有些测试点具有特殊的性质:

  • 前 2 个测试点中不存在任何信号灯。

测试点的输入数据规模:

  • 前 6 个测试点保证 n ≤ 10^3。
  • 所有测试点保证 n ≤ 10^5。

解题要点

模拟题,比前一题稍微复杂,可在前一题基础上修改。

  1. 经过一个完整的红绿黄周期,状态相当于没有任何变化。
    因此通过对整个周期取余,可以大大节省运算时间。
    而后只要进行小于两个周期的模拟,即可得出结果。

  2. 注意模拟循环中:

->红->绿->黄->
对应的数字是
->1->3->2->
不要想当然认为是->1->2->3->,从而写错代码。

  1. 分析数据规模,可知:
  • r、y、g最大为10^6,n最大为10^5,使用int存储没有任何问题。
  • 但结果ans考虑最大的情况(全部遇到为黄灯刚开始)下,为ans=n*(y+r),10^5*(10^6+10^6)=2*10^11次方,
    不可以直接使用int、unsigned int,都达不到要求(可以使用的numeric_limits::min()/max()查询类型T的表示范围)。
    需要使用long long或者unsigned long long替换相关类型。
    如果没有注意数据规模问题,将导致最后两个测试点不通过。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <iostream>
#include <cstdio>
using namespace std;

using timeDuration = unsigned long long;

int stageTime[3];
// ->1->3->2->
void nextState(int &k) {
switch (k) {
case 1: // red
k = 3;
break;
case 2: // yellow
k = 1;
break;
case 3: // green
k = 2;
break;
}
}

void preState(int &k) {
switch (k) {
case 1: // red
k = 2;
break;
case 2: // yellow
k = 3;
break;
case 3: // green
k = 1;
break;
}
}

inline int getStageTime(const int& k) {
return stageTime[k-1];
}

void calcState(timeDuration time, int& k, timeDuration& t) {
const timeDuration cycle = stageTime[0] + stageTime[1] + stageTime[2];
time %= cycle;
if (time < t) {
t -= time;
return;
}
time -= t; nextState(k); // frist part stage
bool proceed = true;
timeDuration stageTime;
while (time > 0) {
stageTime = getStageTime(k);
if (time < stageTime) {
t = stageTime - time; // final part stage
break;
}
time -= stageTime; nextState(k); // proceed entire stage
}
return;
}

int main()
{
int &r = stageTime[0], &y = stageTime[1], &g= stageTime[2];
cin >> r >> y >> g;
timeDuration n; cin >> n;
timeDuration ans = 0;
for (timeDuration i = 0; i < n; i++) {
// clog << "当前时间点:" << ans << endl;
int k; timeDuration t; cin >> k >> t;
if (k == 0) {
ans += t;
continue;
}
// clog << "初始状态:" << k << " " << t << endl;
calcState(ans, k, t);
// clog << "达到状态:" << k << " " << t << endl;
switch (k)
{
case 1: // R
ans += t;
break;
case 2: // Y
ans += t + r;
break;
case 3: // G
ans += 0;
break;
default:
break;
}
}
cout << ans << endl;
return 0;
}

CCF2018-12-1 小明上学

题目说明

题目背景

  小明是汉东省政法大学附属中学的一名学生,他每天都要骑自行车往返于家和学校。为了能尽可能充足地睡眠,他希望能够预计自己上学所需要的时间。他上学需要经过数段道路,相邻两段道路之间设有至多一盏红绿灯。
  京州市的红绿灯是这样工作的:每盏红绿灯有红、黄、绿三盏灯和一个能够显示倒计时的显示牌。假设红绿灯被设定为红灯 r 秒,黄灯 y 秒,绿灯 g 秒,那么从 0 时刻起,[0,r) 秒内亮红灯,车辆不许通过;[r, r+g) 秒内亮绿灯,车辆允许通过;[r+g, r+g+y) 秒内亮黄灯,车辆不许通过,然后依次循环。倒计时的显示牌上显示的数字 l(l > 0)是指距离下一次信号灯变化的秒数。

问题描述

  一次上学的路上,小明记录下了经过每段路的时间,和各个红绿灯在小明到达路口时的颜色和倒计时秒数。希望你帮忙计算此次小明上学所用的时间。

输入格式

  输入的第一行包含空格分隔的三个正整数 r、y、g,表示红绿灯的设置。这三个数均不超过 106。
  输入的第二行包含一个正整数 n(n ≤ 100),表示小明总共经过的道路段数和看到的红绿灯数目。
  接下来的 n 行,每行包含空格分隔的两个整数 k、t。k=0 表示经过了一段道路,耗时 t 秒,此处 t 不超过 10^6;k=1、2、3 时,分别表示看到了一个红灯、黄灯、绿灯,且倒计时显示牌上显示的数字是 t,此处 t 分别不会超过 r、y、g。

输出格式

  输出一个数字,表示此次小明上学所用的时间。

测试用例

样例输入
30 3 30
8
0 10
1 5
0 11
2 2
0 6
0 3
3 10
0 3

样例输出
70

样例说明
  小明先经过第一段道路,用时 10 秒,然后等待 5 秒的红灯,再经过第二段道路,用时 11 秒,然后等待 2 秒的黄灯和 30 秒的红灯,再经过第三段、第四段道路,分别用时6、3秒,然后通过绿灯,再经过最后一段道路,用时 3 秒。共计 10 + 5 + 11 + 2 + 30 + 6 + 3 + 3=70 秒。

评测用例规模与约定

  测试点 1, 2 中不存在任何信号灯。
  测试点 3, 4 中所有的信号灯在被观察时均为绿灯。
  测试点 5, 6 中所有的信号灯在被观察时均为红灯。
  测试点 7, 8 中所有的信号灯在被观察时均为黄灯。
  测试点 9, 10 中将出现各种可能的情况。

解题要点

模拟题,非常简单,模拟情况即可。分析数据规模可知int即可表示所有情况。

注意,黄灯时间结束后还要等红灯结束,才是绿灯可通行。
因此遇到黄灯需要为消耗时间加上 黄灯剩余时间+红灯时间。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
using namespace std;

int main()
{
int r, y, g; cin >> r >> y >> g;
int n; cin >> n;
int ans = 0;
for (int i = 0; i < n; i++) {
// clog << "当前时间点:" << ans << endl;
int k, t; cin >> k >> t;
if (k == 0) {
ans += t;
continue;
}
// clog << "状态:" << k << " " << t << endl;
switch (k)
{
case 1: // R
ans += t;
break;
case 2: // Y
ans += t + r;
break;
case 3: // G
ans += 0;
break;
default:
break;
}
}
cout << ans << endl;
return 0;
}

关于软件工程过度设计的一点感想

代码架构设计的复杂性就像一个左右摇摆的天平。
过于简单,则越有可能产生不好的代码,增大代码修改的难度(需要理解原有不良代码),降低代码维护性。
过于复杂,则产生无用的抽象层,增大代码修改的复杂度(需要理解原有抽象设计并保持),同样会降低代码维护性。
过度设计,其实就是把维护烂代码吃的屎,转成了维护烂设计吃的屎。

有个关键的问题:对于一个设计,想让它被使用起来越爽,相应要去维护它也越累。
我们有时常常无视了设计带来的维护复杂度,只看到了它的使用便利性。我们为软件工程的各种设计而兴奋,梦想着在良好设计的代码库中高效工作而兴奋。

实际上,天下没有白吃的午餐。必须认识到维护设计也有成本,势必会有人为此承担债务,但不代表软件设计就毫无用处,适当的设计可以使使用便利性的好处高于维护复杂性的坏处。

业务代码在设计上要更多地权衡利弊,因为使用者和维护者是同一主体,两者此消彼长,但却也不是等额增减。争取设计带来的使用便利性大于维护复杂性。如果两者相等,设计便是无用的。如果使用便利性小于维护复杂度,那这个起反作用的过度设计应当尽快被移除。
特别地,对于基础通用类库,与业务代码不同,抽象设计的使用者和维护者不是同一批人,值得在设计的天平上,比业务代码更多地倾向良好设计。基础类库的维护者可以通过好设计,将广大使用者的债务负担,转移至维护者一次性解决或削减。

博客重建说明

旧博客的源文件已经完全丢失,故重建博客。

关于旧文章

原博客迁移至域名old.blog.moew.xyz,继续保留开放,但是无意外不会再做修改。

有价值的文章后续会转移过来,这边也会陆续完善。

关于友情链接

已知需要更新的友情链接,已经全部更新完毕。

有几个站无法访问,又联系不到人,只好撤除链接。
如果站点还在需要重新添加,可以联系我。

转换罗马数字

基本思路:
由于最大单位为由M表示的1000,所以千、百、十、个的各个数值均有有限的固定组合且数量不大。
进而1000以下的组合可通过查表拼接完成转换,1000以上部分可通过动态生成重复M的拼接。

首先通过取余循环分离各位数字,而后计算索引值并查表取出对应字符串进行拼接,最后返回结果

JS实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
    function convert(num) {
var rest = num;
var numArr = [];
var table = {
0:'',

1:'I', 2:'II', 3:'III', 4:'IV', 5:'V',
6:'VI', 7:'VII', 8:'VIII', 9:'IX',

10: 'X', 20:'XX', 30:'XXX', 40:'XL', 50:'L',
60:'LX', 70:'LXX', 80: 'LXXX', 90: 'XC',

100:'C', 200: 'CC', 300:'CCC', 400: 'CD', 500:'D',
600:'DC', 700:'DCC', 800: 'DCCC', 900: 'CM',

1000:'M'
};
//分离各位数字
while(rest > 0){
numArr.push(Math.floor(rest % 10));
rest = Math.floor(rest / 10);
}
console.log(numArr);
var result = numArr.reduce(function(str, val, index){
//index代表原始数据从个位起索引
if (index < 3){
//1000以下部分查表转换并拼接
return table[val*Math.pow(10,index)] + str;
}else{
//1000以上部分动态生成
var unit = table[1000].repeat(Math.pow(10,index-3));
return unit.repeat(val) + str;
}
}, '');
console.log(result);
return result;
}

//test case
console.assert(convert(2)==="II");
console.assert(convert(9)==="IX");
console.assert(convert(12)==="XII");
console.assert(convert(29)==="XXIX");
console.assert(convert(500)==="D");
console.assert(convert(501)==="DI");
console.assert(convert(649)==="DCXLIX");
console.assert(convert(891)==="DCCCXCI");
console.assert(convert(999)==="CMXCIX");
console.assert(convert(1000)==="M");
console.assert(convert(1004)==="MIV");
console.assert(convert(2017)==="MMXVII");
console.assert(convert(3999)==="MMMCMXCIX");
console.assert(convert(10000)==="MMMMMMMMMM");
console.assert(convert(16234)==="MMMMMMMMMMMMMMMMCCXXXIV");
console.assert(convert(316234)==="MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMCCXXXIV");

参考资料:Roman Numerals

  • Copyrights © 2014-2022 听寒
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信