Task 7:递归 + 回溯 + 分治 + 动态规划

【递归】
通过LeetCode上【70. 爬楼梯】学习(建议)

class Solution(object):
    def climbStairs(self, n):
        """
        :type n: int
        :rtype: int
        
        """
        condition = [0] * (n + 1)
        condition[0] = 1
        condition[1] = 1
        for i in range(2, n+1):
            condition[i] = condition[i-1] + condition[i-2]
        return condition[n]

【回溯】
利用回溯算法求解八皇后问题


"""
八皇后问题:
回溯算法
"""
# 设置棋盘的大小规模
max_coordinate = 8
# 构建数据结构
# 初始化坐标列表,
# 列表的value为纵坐标值(即棋盘上行数的标记值),
# 列表的index为横坐标值(即棋盘列数标记值)
queen_list = [None]*8
 
 
def show():
    column = 0
    # 对列进行遍历,打印坐标
    while column < max_coordinate:
        print("(%d, %d)" % (column, queen_list[column]), end=" ")
        column += 1
    print("")
 
 
# 对第棋盘上第column列的情况进行检查,看看是否能够放置皇后
def check(column):
    column_2 = 0
    # 对比column小的列进行遍历
    while column_2 < column:
        # 如果比column小的列中有和column对应的queen_list的值相等(即在同一行),
        # 或者有二者的行标记之差的绝对值等于列标记之差的情况(即在其对角线上),
        # 则不能放置该皇后
        if (queen_list[column_2] == queen_list[column]) or (abs(queen_list[column_2] - queen_list[column]) == column - column_2):
            return False
        column_2 += 1
    # 否则,可以放置该皇后
    return True
 
 
# 回溯算法的递归函数主体
# 传入一个初始的横坐标值(即对应棋盘上的列数的标记值)
def put_queen(column):
    row = 0
    # 
    # 对棋盘的行进行遍历:0~7行
    while row < max_coordinate:
        # 假设该皇后可以放置在棋盘的第row行,第column列上
        queen_list[column] = row
        # 对第棋盘的column列进行检查,如果满足条件则进行下一列的放置
        if check(column):
            # 如果已至最后一列,则调用显示方法,打印结果,跳出递归
            if column == max_coordinate - 1:
                show()
            else:
                # 如果未至最后一列,则递归调用自身,实现在下一列中放置另一个皇后
                put_queen(column + 1)
        row += 1
 
 
def main():
    put_queen(0)
    print("="*10)
 
 
if __name__ == '__main__':
    main()

"""
八皇后问题
递归回溯算法
"""
 
def queen(queen_list, current_column=0):
    
    for row in range(len(queen_list)):
        # 如果已至最后一列,则打印结果,跳出递归
        if current_column == len(queen_list):
            for i in range(len(queen_list)):
                print("(%d, %d)" % (i, queen_list[i]), end=" ")
            print("")
            return 
 
        # 假设当前列能够放置一个皇后,用queen_list的index记录列标,value记录行标
        # flag为可行性的标记
        queen_list[current_column],flag = row,True
        # 对当前列之前的各列进行遍历
        for column in range(current_column):
            # 排除同行及对角线上的位置,将flag设置为False
            if (queen_list[column] == row) or (abs(row - queen_list[column]) == current_column - column):
                flag = False
                # 只要有一个不满足的条件,就跳出遍历
                break
        # 如果可以放置,则递归调用自身,对下一列进行筛选
        if flag:
            queen(queen_list, current_column + 1)
 
queen([None]*8)
 

利用回溯算法求解 0-1 背包问题

bestV = 0 # 最大价值
currW = 0 # 当前背包重量
currV = 0 # 当前背包价值
bestx = None # 最优解路径
def backtrack(i):
    global bestV,bestx,currV,currW,x
    if i>= n:
        if bestV<currV:
            bestV = currV
            bestx = x[:]
    else:
        if currW+w[i]<=c:
            x[i]=1
            currW += w[i]
            currV += v[i]
            backtrack(i+1)
            currW -= w[i]
            currV -= v[i]
        x[i]=0
        backtrack(i+1)
if __name__=='__main__':
	n=6
	c=10
	w = [2, 2, 3, 1, 5, 2]
	v = [2, 3, 1, 5, 4, 3]
	x = [0 for i in range(n)]
    backtrack(0)
    print(bestV) #最大价值
    print([i+1  for i in range(n) if bestx[i]]) #最优解路径
    ## 输出
    # 15
	# [2, 4, 5, 6]

【分治】
利用分治算法求一组数据的逆序对个数


#include "stdio.h"
int count=0;
void Merge(int r[],int r1[],int s,int m,int t)	// 合并子序列
{
	int i=s,j=m+1,k=s;
	int b;
	while(i<=m && j<=t){
		if(r[i]<=r[j]){       // 取较小者放入r1[k]中
			r1[k++]=r[i++];
		}
		else{                 	
			count+=m-i+1;	// 若左边数大于右边数,则左边数及其后边数都大于该右边数		
			b=i;              
			while(b<=m){
				printf("[%d,%d]\n",r[b],r[j]);
				b++;
			}
			r1[k++]=r[j++];
		}
 
	}
	while(i<=m)			// 若第一个子序列没处理完,则进行收尾处理;下同
		r1[k++]=r[i++];
	while(j<=t)
		r1[k++]=r[j++];
}
void MergeSort(int r[],int s,int t){  // 对序列r[s]~r[t]进行归并排序
	int m,r1[1000],i;
	if(s==t)
		return ;
	else{
		m=(s+t)/2;	       // 划分
		MergeSort(r,s,m);     // 子问题1
		MergeSort(r,m+1,t);   // 子问题2
		Merge(r,r1,s,m,t);    // 合并
		for(i=s;i<=t;i++)
			r[i]=r1[i];
	}
}
int main(){
	int r[]={23,13,35,6,19,50,28,38,26,17,45},i;
	MergeSort(r,0,10);		// 三个参数分别为待查数组、起始下标、截止下标
	for(i=0;i<=10;i++)
		printf("%d ",r[i]);
	printf("\n一共 %d个逆序对\n",count);
}

【动态规划】
0-1 背包问题

def bag(n, c, w, v):
	value = [[0 for j in range(c + 1)] for i in range(n + 1)]
	for i in range(1, n + 1):
		for j in range(1, c + 1):
			if j < w[i - 1]:
				value[i][j] = value[i - 1][j]
			else:
				value[i][j] = max(value[i - 1][j], value[i - 1][j - w[i - 1]] + v[i - 1])
		# 背包总容量够放当前物体,取最大价值
	return value
def show(n, c, w, value):
    print('最大价值为:', value[n][c])
    x = [0 for i in range(n)]
    j = c
    for i in range(n, 0, -1):
        if value[i][j] > value[i - 1][j]:
            x[i - 1] = 1
            j -= w[i - 1]
    print('背包中所装物品为:')
    for i in range(n):
        if x[i]:
            print('第', i+1, '个,', end='')
if __name__=='__main__':
	n=6
	c=10
	w = [2, 2, 3, 1, 5, 2]
	v = [2, 3, 1, 5, 4, 3]
	value = bag(n,c,w,v)
	show(n, c, w, value)
# 输出
'''
最大价值为: 15
背包中所装物品为:
第 2 个,第 4 个,第 5 个,第 6 个,
'''

最小路径和(详细可看 Minimum Path Sum)

 class Solution:
        def minPathSum(self, grid):
            """
            :type grid: List[List[int]]
            :rtype: int
            """
            n = len(grid)
            m = len(grid[0])
            for i in range(1,n):
                grid[i][0] = grid[i-1][0] + grid[i][0]   #首先需要寻找左边界各点的路径总和
        
            for j in range(1,m):
                grid[0][j] = grid[0][j-1] + grid[0][j]  #寻找上边界各点的路径总和
     
            for i in range(1,n):
                for j in range(1,m):
                    grid[i][j] = min(grid[i-1][j] , grid[i][j-1]) + grid[i][j]  #以边界处为依据一步步推出内部个点的路径总和
  
            return grid[n-1][m-1]

编程实现莱文斯坦最短编辑距离

import numpy
 
def wer2(r, h):
    '''
    This function was originally written by Martin Thoma
    https://martin-thoma.com/word-error-rate-calculation/
    Calculation of WER with Levenshtein distance.
    Works only for iterables up to 254 elements (uint8).
    O(nm) time ans space complexity.
    Parameters
    ----------
    r : list
    h : list
    Returns
    -------
    int
    Examples
    --------
    >>> wer("who is there".split(), "is there".split())
    1
    >>> wer("who is there".split(), "".split())
    3
    >>> wer("".split(), "who is there".split())
    3
    '''
    # Initialization
 
    #生成一个全是0的二维数组
    d = numpy.zeros((len(r)+1)*(len(h)+1), dtype=numpy.uint8)
    print(d)
    #此时的d形如:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
    #将二维数组变成n*n的格式
    d = d.reshape((len(r)+1, len(h)+1))
    print(d)
    #此时的的形如:
    """
    [[0 0 0 0]
     [0 0 0 0]
     [0 0 0 0]
     [0 0 0 0]]
    """
    #为数组两侧赋值
    for i in range(len(r)+1):
        for j in range(len(h)+1):
            if i == 0:
                d[0][j] = j
            elif j == 0:
                d[i][0] = i
    print(d)
    #此时的d形如:
    """
    [[0 1 2 3]
     [1 0 0 0]
     [2 0 0 0]
     [3 0 0 0]
     ]
    """
    # Computation
    #计算编辑距离
    for i in range(1, len(r)+1):
        for j in range(1, len(h)+1):
            if r[i-1] == h[j-1]:
                d[i][j] = d[i-1][j-1]
            else:
                #替换
                substitution = d[i-1][j-1] + 1
                #插入
                insertion    = d[i][j-1] + 1
                #删除
                deletion     = d[i-1][j] + 1
                print(substitution,insertion,deletion)
                d[i][j] = min(substitution, insertion, deletion)
    print(d)
    #最终的d形如:
    """
    [[0 1 2 3]
     [1 1 2 3]
     [2 2 1 2]
     [3 3 2 2]]
    """
    return d[len(r)][len(h)]
if __name__ == '__main__':
    res=wer2(['c','b','c'],['a','b','d'])
    print(res)

编程实现查找两个字符串的最长公共子序列

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int arr[1001][1001];
string ReverseSentence(string str) {
    string res = "", tmp = "";
    for (int i = 0; i < str.size(); ++i) {
        if (str[i] == ' ') res = " " + tmp + res, tmp = "";
        else tmp += str[i];
    }
    if (tmp.size()) res = tmp + res;
    return res;
}

int main() {
    string str1, str2;
    while (getline(cin, str1)) {
        str2 = ReverseSentence(str1);
        int al = str1.size();
        int bl = str2.size();
        for (int i = 1; i <= al; i++)
        {
            for (int j = 1; j <= bl; j++)
            {
                if (str1[i - 1] == str2[j - 1]) 
                    arr[i][j] = arr[i - 1][j - 1] + 1; 
                else  
                    arr[i][j] = max(arr[i - 1][j], arr[i][j - 1]);
            }
        }
        cout << arr[al][bl];
    }
    return 0;

}

编程实现一个数据序列的最长递增子序列

import numpy as np 
from numpy import *
 
class LIS(object):
    '''
    动态规划实现n^2和nlogn算法实现获得【最长公共子序列】(LIS)
    list:作为计算LIS的数组,可由用户输入,也可由程序生成
    '''
 
    def __init__(self, list=[]):
        self.list = list
 
    #函数说明:用复杂度为n2的DP算法计算LIS长度。用了2层嵌套循环
                # 外层循环用来逐个扫描输入,假设当前扫描到的元素是X
                # 内层循环用来找出在X的左边(也就是已经扫描过的),且值比X小的元素E,使X能拼接到以E结尾的LIS的后面
                # 参考博客:https://www.cnblogs.com/zhangbaochong/p/5793965.html
    #参数:self.list
    #返回值:LIS的长度
    def lis_length_dp_n2(self):
        list_len = len(self.list)
        if list_len < 1:
            return
        pos = [-1]*list_len         # pos[i]代表以self.list[i]结尾的最长上升子序列的上一个序列内数的下标
        dp_len = [1]*list_len       # dp_len[i]代表以self.list[i]结尾的最长上升子序列的长度!!!
        max_length = 1
        for i in range(1, list_len):
            for j in range(0, i):
                if self.list[j] < self.list[i] and dp_len[i] < dp_len[j]+1:
                    dp_len[i] = dp_len[j]+1
                    pos[i] = j
            if max_length < dp_len[i]:
                max_length = dp_len[i]
        max_len = max(dp_len)
        last_pos = dp_len.index(max_len)    #返回最大值对应的下标
        lis = []
        for i in range(max_len):
            lis.append(self.list[last_pos])
            last_pos = pos[last_pos]
        lis.reverse()                       #反转
        print("in lis_length_dp_n2 a LIS is: "+str(lis))
        return max_length
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值