第15章 动态规划
15.1 装配线调度
#include <stdio.h>
#include <stdlib.h>
enum { NUM = 6 };
void fastest_way(int n,int a[][n], int t[][n - 1],
int e[], int x[], int f[][n], int l[][n],
int *fastest_time, int *last_line)
{
f[0][0] = e[0] + a[0][0];
f[1][0] = e[1] + a[1][0];
for (int j = 1; j < n; j++) {
if (f[0][j - 1] <= f[1][j - 1] + t[1][j - 1]) {
f[0][j] = f[0][j - 1] + a[0][j];
l[0][j] = 0;
} else {
f[0][j] = f[1][j - 1] + t[1][j - 1] + a[0][j];
l[0][j] = 1;
}
if (f[1][j - 1] <= f[0][j - 1] + t[0][j - 1]) {
f[1][j] = f[1][j - 1] + a[1][j];
l[1][j] = 1;
} else {
f[1][j] = f[0][j - 1] + t[0][j - 1] + a[1][j];
l[1][j] = 0;
}
}
if (f[0][NUM - 1] + x[0] <= f[1][n - 1] + x[1]) {
*fastest_time = f[0][n - 1] + x[0];
*last_line = 0;
} else {
*fastest_time = f[1][n - 1] + x[1];
*last_line = 1;
}
}
void print_stations(int n,int line[][n], int last_line)
{
int i = last_line;
printf("line %d, station %d\n", i + 1, n);
for (int j = n - 1; j > 0; j--) {
i = line[i][j];
printf("line %d, station %d\n", i + 1, j);
}
}
int main()
{
int n=NUM;
int f[2][NUM];
int l[2][NUM];
int a[2][NUM] = { {7, 9, 3, 4, 8, 4}, {8, 5, 6, 4, 5, 7} };
int t[2][NUM - 1] = { {2, 3, 1, 3, 4}, {2, 1, 2, 2, 1} };
int e[2] = { 2, 4 };
int x[2] = { 3, 2 };
int fastest_time;
int last_line;
fastest_way(n,a, t, e, x, f, l, &fastest_time, &last_line);
printf("%d %d\n", fastest_time, last_line + 1);
printf("输出F数组:\n");
for (int i = 0; i < n; ++i) {
printf("%2d ", f[0][i]);
}
printf("\n");
for (int i = 0; i < n; ++i) {
printf("%2d ", f[1][i]);
}
printf("\n");
printf("输出L数组:\n");
for (int i = 1; i < n; ++i) {
printf("%2d ", l[0][i] + 1);
}
printf("\n");
for (int i = 1; i < n; ++i) {
printf("%2d ", l[1][i] + 1);
}
printf("\n");
print_stations(n,l, last_line);
return 0;
}
15.2 矩阵链相乘
15.2.1 矩阵相乘
#include <stdio.h>
#include <stdlib.h>
typedef struct matrix_type *matrix;
struct matrix_type {
int row;
int col;
int **data;
};
matrix matrix_create(int row, int col)
{
if (row == 0)
return NULL;
matrix m = malloc(sizeof(struct matrix_type));
m->row = row;
m->col = col;
m->data = malloc(sizeof(int *) * row);
for (int i = 0; i < row; i++) {
m->data[i] = malloc(sizeof(int) * col);
for (int j = 0; j < col; j++) {
m->data[i][j] = 0;
}
}
return m;
}
void matrix_destroy(matrix m)
{
for (int i = 0; i < m->row; i++)
free(m->data[i]);
free(m->data);
free(m);
}
void matrix_display(matrix m)
{
for (int i = 0; i < m->row; ++i) {
for (int j = 0; j < m->col; ++j) {
printf("%2d ", m->data[i][j]);
}
printf("\n");
}
}
void matrix_multiply(matrix A, matrix B, matrix C)
{
if (A->col != B->row) {
return;
}
for (int i = 0; i < A->row; ++i) {
for (int j = 0; j < B->col; ++j) {
C->data[i][j] = 0;
for (int k = 0; k < A->col; ++k) {
C->data[i][j] += A->data[i][k] * B->data[k][j];
}
}
}
}
void matrix_copy(matrix mdst, matrix msrc)
{
if (mdst->row != msrc->row || mdst->col != msrc->col) {
matrix_destroy(mdst);
mdst->row = msrc->row;
mdst->col = msrc->col;
mdst->data = malloc(sizeof(int *) * mdst->row);
for (int i = 0; i < mdst->row; i++) {
mdst->data[i] = malloc(sizeof(int) * mdst->col);
}
}
for (int i = 0; i < mdst->row; i++) {
for (int j = 0; j < mdst->col; j++) {
mdst->data[i][j] = msrc->data[i][j];
}
}
}
int main()
{
matrix A = matrix_create(2, 4);
matrix B = matrix_create(4, 3);
for (int i = 0; i < A->row; ++i) {
for (int j = 0; j < A->col; ++j) {
A->data[i][j] = 1; /*全部是1,为了测试方便随便设置的值 */
}
}
printf("输出A矩阵的值:\n");
matrix_display(A);
for (int i = 0; i < B->row; ++i) {
for (int j = 0; j < B->col; ++j) {
B->data[i][j] = 2; /*全部是2,为了测试方便随便设置的值 */
}
}
printf("输出B矩阵的值:\n");
matrix_display(B);
matrix C = matrix_create(A->row, B->col);
matrix_multiply(A, B, C);
printf("输出C矩阵的值:\n");
matrix_display(C);
matrix D = matrix_create(C->row, C->col);
matrix_copy(D, C);
printf("输出D矩阵的值:\n");
matrix_display(D);
matrix_destroy(A);
matrix_destroy(B);
matrix_destroy(C);
matrix_destroy(D);
return 0;
}
15.2.2 求矩阵链的最优加全部括号
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
enum { NUM = 7 };
void matrix_chain_order(int n,int p[], int m[][n], int s[][n])
{
for (int i = 1; i < n; ++i) {
m[i][i] = 0;
}
for (int l = 2; l < n; ++l) {
for (int i = 1; i < n - l + 1; ++i) {
int j = i + l - 1;
m[i][j] = INT_MAX;
for (int k = i; k <= j - 1; ++k) {
int q =
m[i][k] + m[k + 1][j] + p[i -
1] * p[k] * p[j];
if (q < m[i][j]) {
m[i][j] = q;
s[i][j] = k;
}
}
}
}
}
void print_optimal_matrix(int n,int s[][n], int i, int j)
{
if (i == j) {
printf("A%d", i);
} else {
printf("(");
print_optimal_matrix(n,s, i, s[i][j]);
print_optimal_matrix(n,s, s[i][j] + 1, j);
printf(")");
}
}
int main()
{
int n=NUM;
int p[NUM] = { 30, 35, 15, 5, 10, 20, 25 };
int m[NUM][NUM] = { {0} };
int s[NUM][NUM] = { {0} };
matrix_chain_order(n,p, m, s);
printf("最优加全部括号\n");
print_optimal_matrix(n,s, 1, 6);
printf("\n");
printf("输出m数组的值\n");
for (int i = 1; i < n; ++i) {
for (int j = 1; j < n; ++j) {
printf("%5d ", m[i][j]);
}
printf("\n");
}
printf("输出s数组的值\n");
for (int i = 1; i < n; ++i) {
for (int j = 1; j < n; ++j) {
printf("%5d ", s[i][j]);
}
printf("\n");
}
return 0;
}
15.3 动态规划基础
15.3.1 递归求矩阵链的最优加全部括号
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
enum { NUM = 7 };
int recursive_matrix_chain(int n,int p[], int i, int j, int m[][n], int s[][n])
{
if (i == j) {
return 0;
}
m[i][j] = INT_MAX;
for (int k = i; k <= j - 1; k++) {
int q = recursive_matrix_chain(n,p, i, k, m, s)
+ recursive_matrix_chain(n,p, k + 1, j, m, s)
+ p[i - 1] * p[k] * p[j];
if (q < m[i][j]) {
m[i][j] = q;
s[i][j] = k;
}
}
return m[i][j];
}
void print_optimal_matrix(int n,int s[][n], int i, int j)
{
if (i == j) {
printf("A%d", i);
} else {
printf("(");
print_optimal_matrix(n,s, i, s[i][j]);
print_optimal_matrix(n,s, s[i][j] + 1, j);
printf(")");
}
}
int main()
{
int n=NUM;
int p[NUM] = { 30, 35, 15, 5, 10, 20, 25 };
int m[NUM][NUM] = { {0} };
int s[NUM][NUM] = { {0} };
recursive_matrix_chain(n,p, 1, 6, m, s);
printf("最优加全部括号\n");
print_optimal_matrix(n,s, 1, 6);
printf("\n");
printf("输出m数组的值\n");
for (int i = 1; i < n; ++i) {
for (int j = 1; j < n; ++j) {
printf("%5d ", m[i][j]);
}
printf("\n");
}
printf("输出s数组的值\n");
for (int i = 1; i < n; ++i) {
for (int j = 1; j < n; ++j) {
printf("%5d ", s[i][j]);
}
printf("\n");
}
return 0;
}
15.3.2 加了备忘的递归求矩阵链的最优加全部括号
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
enum { NUM = 7 };
int lookup_chain(int n, int p[], int i, int j, int m[][n], int s[][n])
{
if (m[i][j] < INT_MAX) {
return m[i][j];
}
if (i == j) {
m[i][j] = 0;
} else {
for (int k = i; k <= j - 1; ++k) {
int q = lookup_chain(n, p, i, k, m, s)
+ lookup_chain(n, p, k + 1, j, m, s)
+ p[i - 1] * p[k] * p[j];
if (q < m[i][j]) {
m[i][j] = q;
s[i][j] = k;
}
}
}
return m[i][j];
}
int memoized_matrix_chain(int n, int p[], int m[][n], int s[][n])
{
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
m[i][j] = INT_MAX;
}
}
return lookup_chain(n, p, 1, n - 1, m, s);
}
void print_optimal_matrix(int n, int s[][n], int i, int j)
{
if (i == j) {
printf("A%d", i);
} else {
printf("(");
print_optimal_matrix(n, s, i, s[i][j]);
print_optimal_matrix(n, s, s[i][j] + 1, j);
printf(")");
}
}
int main()
{
int n = NUM;
int p[NUM] = { 30, 35, 15, 5, 10, 20, 25 };
int m[NUM][NUM] = { {0} };
int s[NUM][NUM] = { {0} };
memoized_matrix_chain(n, p, m, s);
printf("最优加全部括号\n");
print_optimal_matrix(n, s, 1, 6);
printf("\n");
printf("输出m数组的值\n");
for (int i = 1; i < n; ++i) {
for (int j = 1; j < n; ++j) {
printf("%5d ", m[i][j] == INT_MAX ? 0 : m[i][j]);
}
printf("\n");
}
printf("输出s数组的值\n");
for (int i = 1; i < n; ++i) {
for (int j = 1; j < n; ++j) {
printf("%5d ", s[i][j]);
}
printf("\n");
}
return 0;
}
15.4 最长公共子串
15.4.1 求最长公共子串
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
enum direction_enum {
direction_up,
direction_left,
direction_left_up
};
void lcs_length(char *X, char *Y, int m, int n, int c[][n], int b[][n])
{
for (int i = 0; i < m; ++i) {
c[i][0] = 0;
}
for (int j = 0; j < n; ++j) {
c[0][j] = 0;
}
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
if (X[i] == Y[j]) {
c[i][j] = c[i - 1][j - 1] + 1;
b[i][j] = direction_left_up;
} else {
if (c[i - 1][j] >= c[i][j - 1]) {
c[i][j] = c[i - 1][j];
b[i][j] = direction_up;
} else {
c[i][j] = c[i][j - 1];
b[i][j] = direction_left;
}
}
}
}
}
void print_lcs(int n,char *X, int b[][n], int i, int j)
{
if (i == 0 || j == 0)
return;
if (b[i][j] == direction_left_up) {
print_lcs(n,X, b, i - 1, j - 1);
printf("%c", X[i]);
} else {
if (b[i][j] == direction_up) {
print_lcs(n,X, b, i - 1, j);
} else {
print_lcs(n,X, b, i, j - 1);
}
}
}
int main()
{
char X[] = "0ABCBDAB"; //X,Y的有效字符的位置从1开始,前面的0是用来填充
char Y[] = "0BDCABA";
int m=strlen(X);
int n=strlen(Y);
int c[m][n];
int b[m][n];
lcs_length(X, Y, m, n, c, b);
print_lcs(n,X, b, m-1, n-1);
printf("\n");
printf("输出C数组:\n");
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
printf("%d ", c[i][j]);
}
printf("\n");
}
return 0;
}
15.4.2 求最长公共子串,不使用b数组
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
enum direction_enum {
direction_up,
direction_left,
direction_left_up
};
void lcs_length(char *X, char *Y, int m, int n, int c[][n])
{
for (int i = 0; i < m; ++i) {
c[i][0] = 0;
}
for (int j = 0; j < n; ++j) {
c[0][j] = 0;
}
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
if (X[i] == Y[j]) {
c[i][j] = c[i - 1][j - 1] + 1;
} else {
if (c[i - 1][j] >= c[i][j - 1]) {
c[i][j] = c[i - 1][j];
} else {
c[i][j] = c[i][j - 1];
}
}
}
}
}
void print_lcs(int n,char *X, int c[][n], int i, int j)
{
if (i == 0 || j == 0)
return;
if (c[i][j] == c[i - 1][j - 1] + 1) {
print_lcs(n,X, c, i - 1, j - 1);
printf("%c", X[i]);
} else {
if (c[i][j] == c[i - 1][j]) {
print_lcs(n,X, c, i - 1, j);
} else {
print_lcs(n,X, c, i, j - 1);
}
}
}
int main()
{
char X[] = "0ABCBDAB"; //X,Y的有效字符的位置从1开始,前面的0是用来填充
char Y[] = "0BDCABA";
int m=strlen(X);
int n=strlen(Y);
int c[m][n];
lcs_length(X, Y, m, n, c);
print_lcs(n,X, c, m-1, n-1);
printf("\n");
printf("输出C数组:\n");
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
printf("%d ", c[i][j]);
}
printf("\n");
}
return 0;
}
15.4.3 求最长公共子串,X,Y字符串有效字符从0开始算
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
enum direction_enum {
direction_up,
direction_left,
direction_left_up
};
void lcs_length(char *X, char *Y, int m, int n, int c[][n])
{
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (X[i] == Y[j]) {
if (i > 0 && j > 0) {
c[i][j] = c[i - 1][j - 1] + 1;
} else {
c[i][j] = 1;
}
} else {
int a = i > 0 ? c[i - 1][j] : 0;
int b = j > 0 ? c[i][j - 1] : 0;
if (a >= b) {
c[i][j] = a;
} else {
c[i][j] = b;
}
}
}
}
}
void print_lcs(int n,char *X, int c[][n], int i, int j)
{
if (i == 0 || j == 0)
return;
if (c[i][j] == c[i - 1][j - 1] + 1) {
print_lcs(n,X, c, i - 1, j - 1);
printf("%c", X[i]);
} else {
if (c[i][j] == c[i - 1][j]) {
print_lcs(n,X, c, i - 1, j);
} else {
print_lcs(n,X, c, i, j - 1);
}
}
}
int main()
{
char X[] = "ABCBDAB";
char Y[] = "BDCABA";
int m=strlen(X);
int n=strlen(Y);
int c[m][n];
lcs_length(X, Y, 7, 6, c);
print_lcs(n,X, c, m-1, n-1);
printf("\n");
printf("输出C数组:\n");
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
printf("%d ", c[i][j]);
}
printf("\n");
}
return 0;
}
15.5 最优二叉查找树
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
void optimal_bst(float p[], float q[], int n, float e[][n], float w[][n],
int root[][n])
{
for (int i = 1; i < n + 1; ++i) {
e[i][i - 1] = q[i - 1];
w[i][i - 1] = q[i - 1];
}
for (int l = 1; l < n; ++l) {
for (int i = 1; i < n - l + 1; ++i) {
int j = i + l - 1;
e[i][j] = INT_MAX;
w[i][j] = w[i][j - 1] + p[j] + q[j];
for (int r = i; r <= j; ++r) {
float t = e[i][r - 1] + e[r + 1][j] + w[i][j];
if (t < e[i][j]) {
e[i][j] = t;
root[i][j] = r;
}
}
}
}
}
int main()
{
float p[] = { INT_MAX, 0.15, 0.10, 0.05, 0.10, 0.20 }; //忽略第一个,INT_MAX是随便设置的值
float q[] = { 0.05, 0.10, 0.05, 0.05, 0.05, 0.10 };
int n=sizeof(p)/sizeof(p[0]);
float e[n+1][n];
float w[n+1][n];
int root[n+1][n];
optimal_bst(p, q, n, e, w, root);
printf("输出e数组的值:\n");
for (int i = 1; i < n+1; i++) {
for (int j = 0; j < n; j++) {
if (j == i - 1 || i <= j) {
printf("%-4.2f ",e[i][j]);
}
}
printf("\n");
}
printf("输出w数组的值:\n");
for (int i = 1; i < n+1; i++) {
for (int j = 0; j < n; j++) {
if (j == i - 1 || i <= j) {
printf("%-4.2f ",w[i][j]);
}
}
printf("\n");
}
printf("输出root数组的值:\n");
for (int i = 1; i < n+1; i++) {
for (int j = 1; j < n; j++) {
if (i <= j) {
printf("%4d ",root[i][j]);
}
}
printf("\n");
}
return 0;
}