我的算法是比较水的,所以最近再各种学习。现在整理一下吧 ,这几天主要在看动态规划,实习生笔试的时候淘宝的笔试题里就有一道,当时珍视不会啊,不过还好现在回了哈哈,
不多写废话了。
动态规划:(dynamic programming)与分之法一样是通过组合来解决问题的。
解题步骤
1.描述最优解结构
2.递归定义最优解的值
3.按自底向上的方式计算最优解
4.由计算出的结果构造一个最优解
下面是算法导论中例子
1.我就不对书里的例子分析啦
现在来说下这个问题。
连续子序列最大和问题 就是这样 一个数组中有一串数字,我们求出其中连续和最长的一段 如{1,2,3,4,-2,9,2}最大值为 19
下面我们按照解题步骤来写这个问题
1.设数组下表从1到n,我们在k位置上(0<k<=n)如果我们k位置前的和大于等于 0;我们就在k位置的值就等于k-1和+k值,否则我们的序列就需要重新开始。
2.我们定义 数组 f[i] 用来表示到 i位置处,子序列最大值,a[i]用来表示i位置出的值
则有 : f[i]=f[i-1]+a[i] 如果 f[i-1]>=0
f[i]=a[i] 如果f[i-1]<0;
3,
4.
3.4步省略算法直接在最后给出
package Algorithm.DP;
public class MaxContinuousSubstring {
int [] a;//保存传入的数组
int n;
int [] b;//保存最长子序列
int start=0;//存放最长子序列的起始下标
int maxValue;
int maxIndex;
// int [] c;//用于获得最长子序列的位置
public MaxContinuousSubstring(int []a){
this.a=a;
n=a.length;
b=new int[n];
}
public void handle(){
b[0]=a[0];
for(int i=1;i<a.length;i++){
if(b[i-1]>=0)
b[i]=b[i-1]+a[i];
else
b[i]=a[i];
}
}
//获得最大和与最大元素位置
public void caculate(){
int j=0;
int max=-99999;
for(int i=0;i<b.length;i++){
if(b[i]>max)
{
max=b[i];
j=i;
}
}
maxValue=max;
maxIndex=j;
}
private void print(String s){
System.out.println(s);
}
public void getMaxContinuousIndex(){
int j=0;
for(int i=maxIndex;i>=0;i--){
if(b[i]>=0)
j=i;
else
break;
}
start=j;
}
public void display(){
for(int i=0;i<b.length;i++)
System.out.println(b[i]+" ");
print("最大值"+maxValue+"最长子序列"+start+" "+maxIndex);
}
public static void main(String [] args){
int[]a=new int[]{1,2,3,-1,-20,100,34};
int []b= new int[]{-1,-2,3,4,5,7,9};
int[] c=new int[]{-1,-2,-3,-4,-5,-6};
int []d=new int[]{-5,-8,22,98,0,2};
MaxContinuousSubstring mcs=new MaxContinuousSubstring(d);
mcs.handle();
mcs.caculate();
mcs.getMaxContinuousIndex();
mcs.display();
}
}
结果为
-5
-8
22
120
120
122
最大值122最长子序列2 5
这个问题的时间复杂度为 o(n) 此问题共有 o(n)个子问题,每个子问题有两种选择。所以时间复杂度是 o(n)
下面是背包问题整理
1.01背包
问题定义 有固定容量的背包 有物品i种,有重量属性 c[i], 价值属性w[i]
直接给出状态转移方程 f[i][v]=max{f[i-1][v],f[i-1][v-c[i]+w[i]]}
给出代码
package Algorithm.DP;
/*
* 01 背包问题
*/
public class beibao01 {
private int [][] f;//存储容量信息
private int [][]b;//存储存放的物品信息
private int size;
private int max;
private int []x;
private int []a;
private int []w;
public beibao01(int max,int a[],int w[]){
this.max=max;
this.size=a.length+1;
this.a=a;
this.w=w;
x=new int[size];
f=new int[size][max+1];
b=new int[size][max+1];
}
public void handle(){
for(int i=1;i<size;i++){
for(int v=1;v<max+1;v++)
f[i][v]=0;
}
f=new int[size][max+1];
for(int t=0;t<size;t++)
f[t][0]=0;
for(int j=0;j<max+1;j++)
f[0][j]=0;
for(int i=1;i<size;i++)
for(int v=1;v<max+1;v++)
{
if(v-a[i-1]<0)
f[i][v]=f[i-1][v];
else if(f[i-1][v]>f[i-1][v-a[i-1]]+w[i-1])
f[i][v]=f[i-1][v];
else
f[i][v]=f[i-1][v-a[i-1]]+w[i-1];
}
for(int v=1;v<max+1;v++){
for(int i=1;i<size;i++)
System.out.print(f[i][v]+"");
System.out.println();
}
}
public void traceBack(){
int capacity=max;
for(int i=size-1;i>0;i--){
if(f[i][capacity]==f[i-1][capacity])
// System.out.println(i);
x[i]=0;
else
{
x[i]=1;
System.out.println(a[i-1]+"c"+capacity);
capacity=capacity-a[i-1];
}
}
}
public void printPath(){
System.out.println("选择的物品为");
for(int i=1;i<x.length;i++){
if(x[i]==1)
System.out.print(i+" ");
}
}
public static void main(String [] args){
int []a ={2,1,4,5};
int []w={1,8,8,2};
beibao01 beibao=new beibao01(4, a, w);
//beibao01.traceBack();
beibao.handle();
beibao.traceBack();
beibao.printPath();
// beibao01.display();
}
}
此问题时间复杂度为 0(i*v) 时间复杂度已经不能优化了 那我们来优化空间复杂度
如下这段代码
for(int i=1;i<n;i++)
for(int j=V;i>0;j--)
{ f[v]=max{f[j],f[j-c[i]+w[i]] }
2.
完全背包 这个问题和前面描述差不多 只是每个元素都是可选的
有以下几种解法
基本状态转换方程
f[i][v]=max{f[i-1][v],f[i-1][v-k*c[i]+k*w[i]]} k<=V/c[i];
有如下几种思路来解
1.转换为 01背包 ,元素 i 转 k<=V/c[i]个元素 ,比较容易理解 时间复杂度为 o( ∑(V/c[i]*V)
2.利|用|二|进|制|思|想| 转换 i 为 k*c[i],k*w[i], k满足 2*k<=V; 这样利用二进制思想可以把时间复杂度转化为 o(∑(log(V/c[i])*V)
3 O(p*v)
for(int i=1;i<n;i++)
for(int j=1;i<=v;j++)
{ f[v]=max{f[j],f[j-c[i]+w[i]] }
注意与第一题的对比 ,这里我们可以做一个优化,就是把 所有 重量大并且价值少的给优化掉。
3.下面是多重背包问题 多重背包是 第一个 与第二个问题的组合 ,也就是说元素不可以随便拿了他们有自己的上线 n[i]
状态转换方程
f[i][v]=max{f[i-1][v],f[i-1][v-k*c[i]+k*w[i]]} 0<=k<=n[i]];
解法:
1.转换成 01 背包
2.利用二进制思想
把一个物品拆分成 n 个带有 系数的物品 每个物品 与价值分别为 系数乘以他们
系数分别为 1,2,4,n-1,n[i]-2^k+1;k满足n[i]-2^k+1>0的最大值