dp表的本质:调用M个子函数&&每一次传参有K个可变量👇
建立并维护M个K维表格

本文所有gpt回答都由gpt4生成

Robot Goes to Target

题目一:


完全暴力递归

1
2
3
4
5
6
7
int baoli(int cur, int rest, int target, int length) {
if (length == 1) rest == 0? 1: 0;
if (rest == 0) return (cur == target) ? 1 : 0;
if (cur == 1) return baoli(2, rest - 1, target, length);
if (cur == length) return baoli(length - 1, rest - 1, target, length);
return baoli(cur + 1, rest - 1, target, length) + baoli(cur - 1, rest - 1, target, length);;
}

傻缓存法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{main}vector<vector<int>> dp(N + 1, vector<int>(R + 1, -1));

int process(int cur, int rest, int target, int length, vector<vector<int>> dp) {
if (length == 1) return rest == 0? 1: 0;
if (dp[cur][rest] != -1) return dp[cur][rest];
//之前没算过!

int ans = 0;
if (abs(target - cur) > rest) ans = 0;
else if (rest == 0) ans = (cur == target) ? 1 : 0;
else if (cur == 1) ans = process(2, rest - 1, target, length, dp);
else if (cur == length) ans = process(length -1, rest - 1, target, length, dp);
else ans = process(cur + 1, rest - 1, target, length, dp) + process(cur - 1, rest - 1, target, length, dp);
dp[cur][rest] = ans;
return ans;
}

逆向直接dp表外挂法

1
2
3
4
5
6
7
8
9
10
11
12
13
int zhengbiaoshengcheng(int cur, int rest, int target, int length) {
if (length == 1) return rest == 0 ? 1 : 0;
vector<vector<int>> dp1(length + 1, vector<int>(rest + 1, 0));
dp1[target][0] = 1;

for (int i = 1; i <= rest; i++) {
dp1[1][i] = dp1[2][i - 1];
for (int j = 2; j < length; j++)
dp1[j][i] = dp1[j + 1][i - 1] + dp1[j - 1][i - 1];
dp1[length][i] = dp1[length - 1][i - 1];
}
return dp1[cur][rest];
}
原理一句话: 由 "纯暴力"(第一个解法) 法进一步修正得到 . 直接上图

先把第一列填好, 然后从左往右, 从上到下, 根据暴力方法的代码 所推导出格子之间的依赖, 把数值填好. 最后, 返回dp1[cur][rest];

入参判断&&分析bug来源

用对数器看着Test Over. 跑完, 真爽!🤭

下面, 是带对数器的完整的代码~
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
#include <bits/stdc++.h>

using namespace std;

void set4nums(int& M, int& R, int& T, int& N) {
N = rand() % 30 + 1;
M = rand() % N + 1;
if (M > N) {
M ^= N;
N ^= M;
M ^= N;
}
T = rand() % N + 1;
R = rand() % 10;
}

int baoli(int cur, int rest, int target, int length) {
if (length == 1)
return rest == 0 ? 1 : 0;
if (rest == 0)
return (cur == target) ? 1 : 0;
if (cur == 1)
return baoli(2, rest - 1, target, length);
if (cur == length)
return baoli(length - 1, rest - 1, target, length);
return baoli(cur + 1, rest - 1, target, length) + baoli(cur - 1, rest - 1, target, length);
;
}

int process(int cur, int rest, int target, int length, vector<vector<int>> dp) {
if (length == 1)
return rest == 0 ? 1 : 0;
if (dp[cur][rest] != -1)
return dp[cur][rest];
// 之前没算过!

int ans = 0;
if (abs(target - cur) > rest)
ans = 0;
else if (rest == 0)
ans = (cur == target) ? 1 : 0;
else if (cur == 1)
ans = process(2, rest - 1, target, length, dp);
else if (cur == length)
ans = process(length - 1, rest - 1, target, length, dp);
else
ans = process(cur + 1, rest - 1, target, length, dp) + process(cur - 1, rest - 1, target, length, dp);
dp[cur][rest] = ans;
return ans;
}
// 1 8 1 1
// 1 1 1 1
// 1 2 1 1
int zhengbiaoshengcheng(int cur, int rest, int target, int length) {
if (length == 1)
return rest == 0 ? 1 : 0;
vector<vector<int>> dp1(length + 1, vector<int>(rest + 1, 0));
dp1[target][0] = 1;

for (int i = 1; i <= rest; i++) {
dp1[1][i] = dp1[2][i - 1];
for (int j = 2; j < length; j++)
dp1[j][i] = dp1[j + 1][i - 1] + dp1[j - 1][i - 1];
dp1[length][i] = dp1[length - 1][i - 1];
}
return dp1[cur][rest];
}

int main() {
srand(time(0));
int test = 10000;
while (test--) {
int M, R, T, N;
set4nums(M, R, T, N);
int bao = baoli(M, R, T, N);
vector<vector<int>> dp(N + 1, vector<int>(R + 1, -1));
int pro = process(M, R, T, N, dp);
int zheng = zhengbiaoshengcheng(M, R, T, N);
if (pro != zheng || pro != bao) {
cout << "Oops!" << endl;
cout << M << R << T << N;
}
return 0;
}
}

以下是对于学习编程以来的遗留问题的解决

二维数组的传递

Two Wise Poker Men

题目:


完全暴力递归

f函数: 我是先手.我要考虑,拿左边或者右边以后, 我作为后手能拿到的分数, 然后选择较大的那个(所以取max).
g函数: 我是后手.我要在对方拿完以后,尝试拿到剩下的牌中的最多的分数,但是,对方只会让我被迫选择较小的那个(所以取min).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ways1 : 递归解法

// ways 1
int f1(int* arr, int L, int R) {
if (L == R)
return arr[L];
int p1 = arr[L] + g1(arr, L + 1, R);
int p2 = arr[R] + g1(arr, L, R - 1);
return max(p1, p2);
}

int g1(int* arr, int L, int R) {
if (L == R)
return 0;
int p1 = f1(arr, L + 1, R);
int p2 = f1(arr, L, R - 1);
return min(p1, p2);
}

int ways1(int* arr, int length) {
int xianshou = f1(arr, 0, length - 1);
int houshou = g1(arr, 0, length - 1);
return max(xianshou, houshou);
}

傻缓存法

这里,dp表格必须要两个, 为什么? 子函数有 f 和 g , 所以每个子函数自己配备一个dp表.

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

// ways 2
int ways2(int* arr, int length) {
vector<vector<int>> fmap(length, vector<int>(length, -1));
vector<vector<int>> gmap(length, vector<int>(length, -1));

int xianshou = f2(arr, 0, length - 1, fmap, gmap);
int houshou = g2(arr, 0, length - 1, fmap, gmap);
return max(xianshou, houshou);
}
int f2(int* arr, int L, int R, vector<vector<int>>& fmap, vector<vector<int>>& gmap) {
if (fmap[L][R] != -1)
return fmap[L][R];

int ans = 0;
if (L == R)
ans = arr[L];
else {
int p1 = arr[L] + g2(arr, L + 1, R, fmap, gmap);
int p2 = arr[R] + g2(arr, L, R - 1, fmap, gmap);
ans = max(p1, p2);
}
fmap[L][R] = ans;
return ans;
}

int g2(int* arr, int L, int R, vector<vector<int>>& fmap, vector<vector<int>>& gmap) {
if (gmap[L][R] != -1)
return gmap[L][R];

int ans = 0;

if (L != R) {
int p1 = f2(arr, L + 1, R, fmap, gmap);
int p2 = f2(arr, L, R - 1, fmap, gmap);
ans = min(p1, p2);
}
gmap[L][R] = ans;
return ans;
}

严格表递推法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ways 3 严格表递推
int ways3(int* arr, int length) {
vector<vector<int>> fmap(length + 1, vector<int>(length + 1, 0));
vector<vector<int>> gmap(length + 1, vector<int>(length + 1, 0));
for (int i = 0; i < length; i++)
fmap[i][i] = arr[i];
for (int startColumn = 1; startColumn < length; startColumn++) {
int left = 0;
int right = startColumn;
while (right < length) {//行会先超范围
fmap[left][right] = max(arr[left] + gmap[left + 1][right], arr[right] + gmap[left][right - 1]);
gmap[left][right] = min(fmap[left + 1][right], fmap[left][right - 1]);

left++;
right++;
}
}
return max(fmap[0][length - 1], gmap[0][length - 1]);
}
同样, 对数器跑完. 上完整代码
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include <bits/stdc++.h>

using namespace std;


//generate ramdom array

int* generateramdomarray(int & length) {

length = rand() % 19 + 1;
int* arr = new int[length];
for (int i = 0; i < length; i++)
arr[i] = rand() % 6 + 1;
return arr;
}


//ways1 : 递归解法
int f1(int* arr, int L, int R);
int g1(int* arr, int L, int R);
int ways1(int* arr, int);

//ways2 : 傻缓存
int f2(int* arr, int L, int R, vector<vector<int>>&, vector<vector<int>>&);
int g2(int* arr, int L, int R, vector<vector<int>>&, vector<vector<int>>&);
int ways2(int* arr, int);

//ways3 : 严格表递推

int ways3(int* arr, int length);

//ways4 : 纯暴力




/*
0 1 1 4 1 3 3 2 5 4 4 2 0
14
16
16
5 3 1 5 0 3
11
13
13*/

int main() {
//int arr[5] = { 1, 5, 3, 2, 4 };
int arr1[10] = {3, 1 ,6 ,3 ,7 ,29 ,10 , 9 , 8 ,30};

srand(time(0));
//srand(0);
int test = 10000;
while (test--)
{
int length = 0;
int* arr = generateramdomarray(length);
//for (int i = 0; i < length; i++)
//cout << arr[i] << " ";
//cout << endl;

int w1 = ways1(arr, length);
int w2 = ways2(arr, length);
int w3 = ways3(arr, length);
if (w1 != w2 || w1 != w3) {
cout << "Oops!";
cout << w1 << endl;
cout << w2 << endl;
cout << w3 << endl;
}
delete[] arr;
}
cout << "Test Over!" << endl;
//cout << "in 2 " << endl;

return 0;
}

//ways 1
int f1(int* arr, int L, int R) {
if (L == R) return arr[L];
int p1 = arr[L] + g1(arr, L + 1, R);
int p2 = arr[R] + g1(arr, L, R - 1);
return max(p1, p2);
}

int g1(int* arr, int L, int R) {
if (L == R) return 0;
int p1 = f1(arr, L + 1, R);
int p2 = f1(arr, L, R - 1);
return min(p1, p2);
}

int ways1(int* arr, int length) {
int xianshou = f1(arr, 0, length - 1);
int houshou = g1(arr, 0, length - 1);
return max(xianshou, houshou);
}



//ways 2
int ways2(int* arr, int length) {
vector<vector<int>> fmap(length , vector<int>(length , -1));
vector<vector<int>> gmap(length , vector<int>(length , -1));

int xianshou = f2(arr, 0, length-1, fmap, gmap);
int houshou = g2(arr, 0, length-1, fmap, gmap);
return max(xianshou, houshou);
}
int f2(int* arr, int L, int R, vector<vector<int>>& fmap, vector<vector<int>>& gmap) {
if (fmap[L][R] != -1) return fmap[L][R];

int ans = 0;
if (L == R) ans = arr[L];
else {
int p1 = arr[L] + g2(arr, L + 1, R, fmap, gmap);
int p2 = arr[R] + g2(arr, L, R - 1, fmap, gmap);
ans = max(p1, p2);
}
fmap[L][R] = ans;
return ans;

}

int g2(int* arr, int L, int R, vector<vector<int>>& fmap, vector<vector<int>>& gmap) {
if (gmap[L][R] != -1) return gmap[L][R];

int ans = 0;

if (L != R) {
int p1 = f2(arr, L + 1, R, fmap, gmap);
int p2 = f2(arr, L, R - 1, fmap, gmap);
ans = min(p1, p2);
}
gmap[L][R] = ans;
return ans;

}

//ways 3 严格表递推
int ways3(int* arr, int length) {
vector<vector<int>> fmap(length + 1, vector<int>(length + 1, 0));
vector<vector<int>> gmap(length + 1, vector<int>(length + 1, 0));
for (int i = 0; i < length; i++)
fmap[i][i] = arr[i];
for (int startColumn = 1; startColumn < length; startColumn++) {
int left = 0;
int right = startColumn;
while (right < length) {
fmap[left][right] = max(arr[left] + gmap[left + 1][right], arr[right] + gmap[left][right - 1]);
gmap[left][right] = min(fmap[left + 1][right], fmap[left][right - 1]);

left++;
right++;
}
}
return max(fmap[0][length - 1], gmap[0][length - 1]);
}

整个过程中, 有时出现过很多次三种ways差那么1~2分(上面完整代码里注释的部分).仔细查看原函数才发现我ways1调用f和g时, L 传的时0, 但是R 传的是 length. 所以其实读取范围超出了数组本身范围. 但是c++的尿性又不会像java一样RangeException之类的报错, 所以就出现能跑但是结果不对的情况.
不过呢, 由于可能length位置的数字是0, 导致有些情况结果是正确的.