动态规划法习题整理

动态规划法

1.POJ1458

求最长公共子序列问题(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;
}

2.POJ1050

题目:

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;
}

3.最大数乘

题目

动态规划法习题整理_第1张图片

算法思路

首先有

  • f[i][j]代表前 i 个数有 j 个乘号的最大值
  • sum[i]代表前i个数之和
  • 对于所有的i,都有f[i][0] = sum[i];其他初始化为0

然后

f[i][j] = max{f[i][j],f[l-1][j-1] * (sum[i] - sum[l-1])}

对于每一组i,j,都用 l 来遍历新增一个乘号的所有位置

Python代码

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])

4.最小化子序列最大值之和

题目:

动态规划法习题整理_第2张图片

算法思路:

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])

5.树着色

题目:

在这里插入图片描述

算法思路:

构造好我们的⼆叉树后,⾃底向上找到尽量多的结点着⿊色的⽅法,i从最下层结点开始。

  • f[i][0]代表以i为根结点且i为⽩色时的所求值
  • f[i][1]代表以i为根结点且i为⿊色时的所求值

有递推公式:

  • f[i][0] = 0 + f[lchild][1] + f[rchild][1]
  • f[i][1] = 1 + f[lchild][0] + f[rchild][0]

代码:

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]))) + ' 个黑色结点') 

6.自然语言处理

题目:

动态规划法习题整理_第3张图片

算法思路:

本题中,由于quality函数不能显式给出,所以仅给出算法思路。本题与 最小化子序列最大值之和思路非常相似,只不过转换成最大化⼦序列quality和的问题:

dp[i] = max{dp[j-1] + quality(y[j] + y[j+1] + … y[i])}}

  • 其中dp[i]是前i个字符的最佳分词quality和
  • y_i 代表的是字符串里第 i 个字符
  • 1 <= i <= N
  • 1 <= j <= i

7.带权活动选择问题

题目:

动态规划法习题整理_第4张图片

算法思路:

⽤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])

你可能感兴趣的:(算法,C++,Python)