求最长公共子序列问题(LCS),自底向上打印一个矩阵即可。
#include
#include
using namespace std;
//自底向上的动态规划求最长公共子序列(LCS),也可以用递归
int main(){
int LCS[1000][1000];
string X,Y;
while (cin >> X >> Y) {
int m = int(X.length());
int n = int(Y.length());
for (int i = 0; i <= m; i++)
LCS[i][0] = 0;
for (int j = 0; j <= n; j++)
LCS[0][j] = 0;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if(X[i-1] == Y[j-1])
LCS[i][j] = LCS[i-1][j-1] + 1;
else
{
if (LCS[i-1][j] >= LCS[i][j-1])
LCS[i][j] = LCS[i-1][j];
else
LCS[i][j] = LCS[i][j-1];
}
}
}
cout << LCS[m][n] << endl;
}
return 0;
}
Given a two-dimensional array of positive and negative integers, a sub-rectangle is any contiguous sub-array of size 1*1 or greater located within the whole array. The sum of a rectangle is the sum of all the elements in that rectangle. In this problem the sub-rectangle with the largest sum is referred to as the maximal sub-rectangle.
As an example, the maximal sub-rectangle of the array:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
is in the lower left corner:
9 2
-4 1
-1 8
and has a sum of 15.
求矩阵的最⼤块问题,思路是将⼆维矩阵“拍扁”,当成一维数组的最大连续段问题来做。
一维数组的最大连续段问题:
dp[i] = max{dp[i-1] + a[i],a[i]}
在程序中可以巧妙的用一个变量dp代替上面dp[i]的记录方法
#include
#include
using namespace std;
#define MAXSIZE 101
//求一维数组的最大连续段
int maxArray(int a[],int n){
int my_max = -10000;
int dp = a[0];
//每次迭代,dp代表以a[i]结尾的连续段的最大值
for (int i = 1; i < n; i++) {
dp = max(dp + a[i],a[i]);
my_max = max(my_max,dp);
}
return my_max;
}
//求二维矩阵的最大连续块
int maxMatrix(int a[][MAXSIZE],int n){
int temp[MAXSIZE],result = -10000;
//遍历所有行
for (int i = 0; i < n; i++) {
//初始化temp矩阵,用来记录“拍扁”的每列的累加和
for (int j = 0; j < n; j++)
temp[j] = 0;
//遍历第 i 到 n-1 行
for (int k = i; k < n; k++) {
//遍历所有列,累加求和,第j列前k行的累加和存储在temp[j]中
for (int j = 0; j < n; j++)
temp[j] += a[k][j];
//temp为一维数组,可以套maxArray函数
int sum_max = maxArray(temp, n);
result = max(result,sum_max);
}
}
return result;
}
int main(){
int a[MAXSIZE][MAXSIZE];
int n;
cin >> n;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
cin >> a[i][j];
cout << maxMatrix(a, n) << endl;
return 0;
}
首先有
然后
f[i][j] = max{f[i][j],f[l-1][j-1] * (sum[i] - sum[l-1])}
对于每一组i,j,都用 l 来遍历新增一个乘号的所有位置
import numpy as np
v = [1,2,3,4,5]
N = len(v)
K = 2
sum = [0] * (N + 1)
f = np.zeros([N + 1,N])
#sum[i]代表前i个数之和
for i in range(1,N + 1):
sum[i] = sum[i - 1] + v[i - 1]
for i in range(1,N + 1):
f[i][0] = sum[i]
#f[i][j]代表前 i 个数有 j 个乘号的最大值
#按行逐步完成 f 矩阵
for i in range(2,N + 1):
#当有 i 个数的时候,最多只能存在 i-1 个乘号
t = min(i - 1,K)
#遍历 1 到 t 个乘号的情况
for j in range(1,t + 1):
#用 l 遍历新增一个乘号的所有位置
for l in range(2,i + 1):
f[i][j] = max(f[i][j],f[l - 1][j - 1] * (sum[i] - sum[l - 1]))
print(f)
print(f[N][K])
dp[i]代表前i个元素的 最小化子序列最大值之和
dp[i] = min{dp[j-1] + max{a[j],a[j+1]…a[i]}}
其中 1<=i<=N , 对每个 i 都满⾜ a[j]+a[j+1]+…+a[i]<=B AND 0<=j<=i
#最小化子序列最大值之和,动态规划法实现
a = [2,2,2,8,1,8,2,1]
#a = [17,1,16]
B = 17
N = len(a)
dp = [0] * N
dp[0] = a[0]
for i in range(1,N):
j = i
myMax = a[j]
tempSum = 0
minSum = a[j] + dp[j - 1]
#j向前迭代,myMax记录组内最大值,tempSum记录组内元素和,minSum记录总的最小和
#组内元素依次为a[j],a[j+1]......a[i-1],a[i]
while j >= 0 and tempSum + a[j] <= B:
myMax = max(myMax,a[j])
tempSum += a[j]
if j == 0:
#j=0 时不存在dp[j-1],直接当作是0
minSum = min(minSum,myMax)
break
minSum = min(minSum,dp[j - 1] + myMax)
j = j - 1
dp[i] = minSum
print(dp[N - 1])
构造好我们的⼆叉树后,⾃底向上找到尽量多的结点着⿊色的⽅法,i从最下层结点开始。
有递推公式:
import numpy as np
#二叉树结点类
class Node(object):
def __init__(self,elem = -1,lchild = None,rchild = None):
self.elem = elem
self.lchild = lchild
self.rchild = rchild
#二叉树类
class Tree(object):
def __init__(self):
self.root = Node()
self.myQueue = []
self.allQueue = []
#按照层次遍历的顺序构造二叉树使其尽量平衡
def add(self,elem):
node = Node(elem)
if self.root.elem == -1:
self.root = node
self.myQueue.append(self.root)
self.allQueue.append(self.root)
else:
treeNode = self.myQueue[0]
if treeNode.lchild == None:
treeNode.lchild = node
self.myQueue.append(treeNode.lchild)
self.allQueue.append(treeNode.lchild)
else:
treeNode.rchild = node
self.myQueue.append(treeNode.rchild)
self.allQueue.append(treeNode.rchild)
self.myQueue.pop(0)
#返回一个按层次遍历整个二叉树的数组
def getAllQueue(self):
return self.allQueue
#建立一颗有N个结点的树
N = 10
#生成N个数据作为树节点
elems = range(N)
#新建一个树对象
tree = Tree()
for elem in elems:
tree.add(elem)
queue = tree.getAllQueue()
#自底向上找到尽量多的结点着黑色的方法,i从最下层结点开始
#f[i][0]代表以i为根结点且i为白色时的所求值;f[i][1]代表以i为根结点且i为黑色时的所求值
f = np.zeros([N,2])
i = N - 1
while i >= 0:
if queue[i].lchild == None and queue[i].rchild == None:
f[i][0] = 0
f[i][1] = 1
if queue[i].lchild != None and queue[i].rchild == None:
f[i][0] = f[queue[i].lchild.elem][1]
f[i][1] = f[queue[i].lchild.elem][0] + 1
if queue[i].lchild != None and queue[i].rchild != None:
f[i][0] = f[queue[i].lchild.elem][1] + f[queue[i].rchild.elem][1]
f[i][1] = f[queue[i].lchild.elem][0] + f[queue[i].rchild.elem][0] + 1
i = i - 1
print('\n最多可以染 ' + str(int(max(f[0][0],f[0][1]))) + ' 个黑色结点')
本题中,由于quality函数不能显式给出,所以仅给出算法思路。本题与 最小化子序列最大值之和思路非常相似,只不过转换成最大化⼦序列quality和的问题:
dp[i] = max{dp[j-1] + quality(y[j] + y[j+1] + … y[i])}}
⽤T[i]来记录前 i 个活动的最优权重和,则有:
T[i + 1] = max{T[i],T[j] + V_i}
第 j 个活动是 结束时间最接近于当前活动开始时间 的活动
#activity = [[0,2,2],[0,3,3],[2,4,2],[2,5,4],[3,5,2],[4,6,1],[5,7,3]]
activity = [[0,1,2],[1,3,2],[2,4,3],[3,6,2],[5,7,5],[7,8,1]]
N = len(activity)
T = [0] * (N + 1)
#T[i]记录了前 i 个活动的最优权重和
#从T[1]开始直到T[N],对应的是activity[0][...]到activity[N-1][...]
for i in range(0,N):
j = i - 1
temp = activity[i][2]
while j >= 0:
#找到 第一个 结束时间在当前活动开始之前的活动
if activity[j][1] <= activity[i][0]:
temp = max(temp,temp + T[j + 1])
break
j = j - 1
T[i + 1] = max(T[i],temp)
print(T[N])