动态规划总结

本文详细介绍了动态规划在解决最长上升子序列和最长公共子序列问题上的应用,包括优化算法和实现细节。通过逆向思维和状态转移方程,我们探讨了如何在O(n^2)的基础上进一步优化至O(n*logn),并提供了具体的代码实现。

经典问题:

一、最长上升子序列:

     问题描述如下: 
设L=<a1,a2,…,an>是n个不同的实数的序列,L的递增子序列是这样一个子序列Lin=<aK1,ak2,…,akm>,其中k1<k2<…<km且aK1<ak2<…<akm。求最大的m值。 这里采用的是逆向思维的方法,从最后一个开始想起,即先从A[N](A数组是存放数据的数组,下同)开始,则只有长度为1的子序列,到A[N-1]时就有两种情况,如果a[n-1] < a[n] 则存在长度为2的不下降子序列 a[n-1],a[n];如果a[n-1] > a[n] 则存在长度为1的不下降子序列a[n-1]或者a[n]。 
有了以上的思想,DP方程就呼之欲出了(这里是顺序推的,不是逆序的): DP[I]=MAX(1,DP[J]+1)  J=0,1,...,I-1 
但这样的想法实现起来是)O(n^2)的。本题还有更好的解法,就是O(n*logn)。

二、最长公共子序列:

给出两个字符串

a, b

,求它们的最长、连续的公共字串。

 

这很容易就想到以

DP[I][J]

表示

A

串匹配到

I

B

串匹配到

J

时的最大长度。

则:

 

0                              I==0 || J==0

 

DP[I][J]=DP[I-1][J-1]+ 1                  A[I]==B[J]

 

          MAX

DP[I-1][J]

DP[I][J-1]

   

不是以上情况

 

 

但这样实现起来的空间复杂度为

O(n^2)

,而上面的方程只与第

I-1

行有关,所

以可以用两个一维数组来代替。

给出两个字符串

a, b

,求它们的最长、连续的公共字串。

 

这很容易就想到以

DP[I][J]

表示

A

串匹配到

I

B

串匹配到

J

时的最大长度。

则:

 

0                              I==0 || J==0

 

DP[I][J]=DP[I-1][J-1]+ 1                  A[I]==B[J]

 

          MAX

DP[I-1][J]

DP[I][J-1]

   

不是以上情况

 

 

但这样实现起来的空间复杂度为

O(n^2)

,而上面的方程只与第

I-1

行有关,所

以可以用两个一维数组来代替。

给出两个字符串

a, b

,求它们的最长、连续的公共字串。

 

这很容易就想到以

DP[I][J]

表示

A

串匹配到

I

B

串匹配到

J

时的最大长度。

则:

 

0                              I==0 || J==0

 

DP[I][J]=DP[I-1][J-1]+ 1                  A[I]==B[J]

 

          MAX

DP[I-1][J]

DP[I][J-1]

   

不是以上情况

 

 

但这样实现起来的空间复杂度为

O(n^2)

,而上面的方程只与第

I-1

行有关,所

以可以用两个一维数组来代替。

给出两个字符串

a, b

,求它们的最长、连续的公共字串。

 

这很容易就想到以

DP[I][J]

表示

A

串匹配到

I

B

串匹配到

J

时的最大长度。

则:

 

0                              I==0 || J==0

 

DP[I][J]=DP[I-1][J-1]+ 1                  A[I]==B[J]

 

          MAX

DP[I-1][J]

DP[I][J-1]

   

不是以上情况

 

 

但这样实现起来的空间复杂度为

O(n^2)

,而上面的方程只与第

I-1

行有关,所

以可以用两个一维数组来代替。

给出两个字符串

a, b

,求它们的最长、连续的公共字串。

 

这很容易就想到以

DP[I][J]

表示

A

串匹配到

I

B

串匹配到

J

时的最大长度。

则:

 

0                              I==0 || J==0

 

DP[I][J]=DP[I-1][J-1]+ 1                  A[I]==B[J]

 

          MAX

DP[I-1][J]

DP[I][J-1]

   

不是以上情况

 

 

但这样实现起来的空间复杂度为

O(n^2)

,而上面的方程只与第

I-1

行有关,所

以可以用两个一维数组来代替。

给出两个字符串a, b,求它们的最长、连续的公共字串。 
这很容易就想到以DP[I][J]表示A串匹配到I,B串匹配到J时的最大长度。则: 
0                              I==0 || J==0 
DP[I][J]=DP[I-1][J-1]+ 1                  A[I]==B[J]           MAX(DP[I-1][J],DP[I][J-1])   不是以上情况  
但这样实现起来的空间复杂度为O(n^2),而上面的方程只与第I-1行有关,所以可以用两个一维数组来代替。

三、o-1背包问题:

 有N件物品和一个容量为V的背包。第i件物品的大小是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。 
   用DP[I][J] 表示前I件物品放入一个容量为J的背包可以获得的最大价值。则   DP[I][J]= DP[I-1][J]                               ,J<C[I] 
MAX(DP[I-1][J],DP[I-1][J-C[I]]+W[I])  , J>=C[I]   
   这样实现的空间复杂度为O(VN),实际上可以优化到O(V)。以下是代码: const int MAXW=13000;    //最大重量 const int MAXN=3450;     //最大物品数量   
int c[MAXN];     //物品的存放要从下标1开始 int w[MAXN];     //物品的存放要从下标1开始 int dp[MAXW];   
//不需要将背包装满,则将DP数组全部初始化为0 
//要将背包装满,则初始化为DP[0]=0,DP[1]…DP[V]=-1(即非法状态) 

int Packet(int n,int v)

 {     

  int i,j; 
      memset(dp,0,sizeof(dp));    

   for(i=1;i<=n;++i) { 
         

 for(j=v;j>=c[i];--j)

 {  //这里是倒序,别弄错了             

  dp[j]=MAX(dp[j],dp[j-c[i]]+w[i]);   

        }     

      return dp[v];


四、完全背包问题:

有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。 
   很容易可以得到这种状态表示:用DP[I][J] 表示前I件物品放入一个容量为J的背包可以获得的最大价值。则 
  DP[I][J]=MAX(DP[I-1][J],DP[I-1][J-K*C[I]]+K*W[I]) 0<=K*C[I]<=J 这样的复杂度是O(V*Σ(V/c[i])) 
    有更好的做法,那就是利用01背包的优化原理。在优化的代码中,之所以第二重循环是倒序,
是为了防止重复拿,那么只要将其变为顺序即可以重复取。

五、多重背包问题:


有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。 
这题仍然可以用到上一题的思想,DP表示状态与上面的相同。方程为: DP[I][J]=MAX(DP[I-1][J],DP[I-1][J-K*C[I]]+K*W[I]) 
不同的是K的范围,0<=K<=N[I] && 0<=K*C[I]<=J 这样的复杂度为O(V*Σn[i])。

有更好的想法就是先用二进制来划分。将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为
1,2,4,...,2^(k-1),n[i]-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。然后用01背包做,这样的复杂度为O(V*Σlog n[i])。

关键代码:

 const int SIZE=1001; int dp[SIZE]; 
int num[SIZE],c[SIZE],w[SIZE];  //num[i]是I物品的件数,C[I]是费用,W[I]是价值 int MultiPack(int n,int v) 

{  //存入参数,N是物品种类数,V是背包容量   

   int i,j,k; 

     memset(dp,0,sizeof(dp)); 
     for(i=1;i<=n;++i)

 { //存放物品的数组下标从1开始       

   if( c[i]*num[i]>=v )

 {              for(j=c[i];j<=v;++j) 


                 dp[j]=MAX(dp[j],dp[j-c[i]]+w[i]);              }   

      } 
         else {  //使用二进制划分              k=1; 
             while( k<num[i] )

 {                  for(j=v;j>=k*c[i];--j) 


                     dp[j]=MAX(dp[j],dp[j-k*c[i]]+k*w[i]);              

    }                  num[i]-=k;           

       k*=2;           

  } 
             for(j=v;j>=num[i]*c[i];--j)

 { 
                 dp[j]=MAX(dp[j],dp[j-num[i]*c[i]]+num[i]*w[i]);              }         

 }      


     return dp[v]; 


参考:http://wenku.baidu.com/link?url=phnoz8M9fnB6wQ5I4DuYkwOImljgy6n6YeLA0Kl20dKrvWZUEtKymDSQOOXMNABvkXYiH2AnV4VBWJ3DVuxRz8OWKgzczSSa97FiWuqvXFq

问题描述如下

:

 

L=<a1,a2,

,an>

n

个不同的实数的序列,

L

的递增子序列是这样一个子序

Lin=<aK1,ak2,

,akm>

,其中

k1<k2<

<km

aK1<ak2<

<akm

。求最大的

m

值。

 

这里采用的是逆向思维的方法,从最后一个开始想起,即先从

A[N]

A

数组是

存放数据的数组,下同)开始,则只有长度为

1

的子序列,到

A[N-1]

时就有两种情

况,

如果

a[n-1] 

a[n] 

则存在长度为

2

的不下降子序列

 a[n-1],a[n]

如果

a[n-1] 

a[n] 

则存在长度为

1

的不下降子序列

a[n-1]

或者

a[n]

 

有了以上的思想,

DP

方程就呼之欲出了(这里是顺序推的,不是逆序的):

 

DP[I]=MAX

1,DP[J]+1

  J=0,1,...,I-1

 

但这样的想法实现起来是)

O(n^2)

的。本题还有更好的解法,就是

O(n*logn)

利用了长升子序列的性质来优化,以下是优化版的代码:

 

//

最长不降子序

       

 

const int SIZE=500001;

 

int data[SIZE];

 

int dp[SIZE];

 

 

 

//

返回值是最长不降子序列的最大长度

,

复杂度

O(N*logN)

 

int LCS(int n) {            //N

DATA

数组的长度

,

下标从

1

开始

 

    int len(1),low,high,mid,i;

 

 

 

    dp[1]=data[1];    

 

    for(i=1;i<=n;++i) {

 

       low=1;

 

       high=len;

问题描述如下

:

 

L=<a1,a2,

,an>

n

个不同的实数的序列,

L

的递增子序列是这样一个子序

Lin=<aK1,ak2,

,akm>

,其中

k1<k2<

<km

aK1<ak2<

<akm

。求最大的

m

值。

 

这里采用的是逆向思维的方法,从最后一个开始想起,即先从

A[N]

A

数组是

存放数据的数组,下同)开始,则只有长度为

1

的子序列,到

A[N-1]

时就有两种情

况,

如果

a[n-1] 

a[n] 

则存在长度为

2

的不下降子序列

 a[n-1],a[n]

如果

a[n-1] 

a[n] 

则存在长度为

1

的不下降子序列

a[n-1]

或者

a[n]

 

有了以上的思想,

DP

方程就呼之欲出了(这里是顺序推的,不是逆序的):

 

DP[I]=MAX

1,DP[J]+1

  J=0,1,...,I-1

 

但这样的想法实现起来是)

O(n^2)

的。本题还有更好的解法,就是

O(n*logn)

利用了长升子序列的性质来优化,以下是优化版的代码:

 

//

最长不降子序

       

 

const int SIZE=500001;

 

int data[SIZE];

 

int dp[SIZE];

 

 

 

//

返回值是最长不降子序列的最大长度

,

复杂度

O(N*logN)

 

int LCS(int n) {            //N

DATA

数组的长度

,

下标从

1

开始

 

    int len(1),low,high,mid,i;

 

 

 

    dp[1]=data[1];    

 

    for(i=1;i<=n;++i) {

 

       low=1;

 

       high=len;

问题描述如下

:

 

L=<a1,a2,

,an>

n

个不同的实数的序列,

L

的递增子序列是这样一个子序

Lin=<aK1,ak2,

,akm>

,其中

k1<k2<

<km

aK1<ak2<

<akm

。求最大的

m

值。

 

这里采用的是逆向思维的方法,从最后一个开始想起,即先从

A[N]

A

数组是

存放数据的数组,下同)开始,则只有长度为

1

的子序列,到

A[N-1]

时就有两种情

况,

如果

a[n-1] 

a[n] 

则存在长度为

2

的不下降子序列

 a[n-1],a[n]

如果

a[n-1] 

a[n] 

则存在长度为

1

的不下降子序列

a[n-1]

或者

a[n]

 

有了以上的思想,

DP

方程就呼之欲出了(这里是顺序推的,不是逆序的):

 

DP[I]=MAX

1,DP[J]+1

  J=0,1,...,I-1

 

但这样的想法实现起来是)

O(n^2)

的。本题还有更好的解法,就是

O(n*logn)

利用了长升子序列的性质来优化,以下是优化版的代码:

 

//

最长不降子序

       

 

const int SIZE=500001;

 

int data[SIZE];

 

int dp[SIZE];

 

 

 

//

返回值是最长不降子序列的最大长度

,

复杂度

O(N*logN)

 

int LCS(int n) {            //N

DATA

数组的长度

,

下标从

1

开始

 

    int len(1),low,high,mid,i;

 

 

 

    dp[1]=data[1];    

 

    for(i=1;i<=n;++i) {

 

       low=1;

 

       high=len;

内容概要:本文详细介绍了一个基于C++的养老院管理系统的设计与实现,旨在应对人口老龄化带来的管理挑战。系统通过整合住户档案、健康监测、护理计划、任务调度等核心功能,构建了从数据采集、清洗、AI风险预测到服务调度与可视化的完整技术架构。采用C++高性能服务端结合消息队列、规则引擎和机器学习模型,实现了健康状态实时监控、智能任务分配、异常告警推送等功能,并解决了多源数据整合、权限安全、老旧硬件兼容等实际问题。系统支持模块化扩展与流程自定义,提升了养老服务效率、医护协同水平和住户安全保障,同时为运营决策提供数据支持。文中还提供了关键模块的代码示例,如健康指数算法、任务调度器和日志记录组件。; 适合人群:具备C++编程基础,从事软件开发或系统设计工作1-3年的研发人员,尤其是关注智慧养老、医疗信息系统开发的技术人员。; 使用场景及目标:①学习如何在真实项目中应用C++构建高性能、可扩展的管理系统;②掌握多源数据整合、实时健康监控、任务调度与权限控制等复杂业务的技术实现方案;③了解AI模型在养老场景中的落地方式及系统架构设计思路。; 阅读建议:此资源不仅包含系统架构与模型描述,还附有核心代码片段,建议结合整体设计逻辑深入理解各模块之间的协同机制,并可通过重构或扩展代码来加深对系统工程实践的掌握。
内容概要:本文详细介绍了一个基于C++的城市交通流量数据可视化分析系统的设计与实现。系统涵盖数据采集与预处理、存储与管理、分析建模、可视化展示、系统集成扩展以及数据安全与隐私保护六大核心模块。通过多源异构数据融合、高效存储检索、实时处理分析、高交互性可视化界面及模块化架构设计,实现了对城市交通流量的实时监控、历史趋势分析与智能决策支持。文中还提供了关键模块的C++代码示例,如数据采集、清洗、CSV读写、流量统计、异常检测及基于SFML的柱状图绘制,增强了系统的可实现性与实用性。; 适合人群:具备C++编程基础,熟悉数据结构与算法,有一定项目开发经验的高校学生、研究人员及从事智能交通系统开发的工程师;适合对大数据处理、可视化技术和智慧城市应用感兴趣的技术人员。; 使用场景及目标:①应用于城市交通管理部门,实现交通流量实时监测与拥堵预警;②为市民出行提供路径优化建议;③支持交通政策制定与信号灯配时优化;④作为智慧城市建设中的智能交通子系统,实现与其他城市系统的数据协同。; 阅读建议:建议结合文中代码示例搭建开发环境进行实践,重点关注多线程数据采集、异常检测算法与可视化实现细节;可进一步扩展机器学习模型用于流量预测,并集成真实交通数据源进行系统验证。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值