ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

DP相关

2022-08-02 18:35:11  阅读:87  来源: 互联网

标签:背包 int 元素 DP 序列 相关 include dp


背包问题

1.01背包问题

一件物品只能选一次

on^2复杂度

f[i][j]表示只看前i个物品,总体积为j的情况下,总价值最大
f[i][j]:

不选第i件物品
f[i][j]=f[i-1][j]
选第i件物品
f[i][j]=f[i-1][j-v[i]](在前i-1件物品中选择重量为j-v[i]的物品的价值)+w[i](第i件物品的价值)
f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i])

初始状态 f[0][0]不选物品的最大价值是0
#include<iostream>
#include<algorithm>
using namespace std;
int w[1001],v[1001];
int dp[1001][1001];
int main()
{
   int n,W;
   cin>>n>>W;
   
   for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
   for(int i=1;i<=n;i++){
       for(int j=1;j<=W;j++){
           
               dp[i][j]=dp[i-1][j];
           
           if(j>=v[i]){
               dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
           }
       }
   }
   cout<<dp[n][W]<<endl;
}

一维数组

如果只想求W时的最大值
那么设f[0]=0,f[i]=-INF,其他值都从0转移过去
#include<iostream>
#include<algorithm>
using namespace std;
int w[1001],v[1001];
int dp[1001];
int main()
{
    int n,W;
    cin>>n>>W;
    
    for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++){
        for(int j=W;j>=v[i];j--){
			//必须从最大值开始
            dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
        }
    }

    cout<<dp[W]<<endl;
}

当背包容量很大时

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int w[101],v[101];
//把价值看作背包容量,将问题转化为求价值恰好为i时的最小重量d[i],
//求满足要求的最大重量 对应的价值是最大价值
int dp[10001];
const int INF =1e9+1;
int main()
{
    int n,W;
    //cout<<INF;
    cin>>n>>W;
    dp[0]=0;
    int sum=0;//最大重量
    //初始化为最大值
    
    //cout<<dp[2];
    for(int i=1;i<=n;i++){
        cin>>v[i]>>w[i];
        sum+=v[i];
    }
    for(int i=1;i<=sum;i++)dp[i]=INF;
    for(int i=1;i<=n;i++){
        for(int j=sum;j>=v[i];j--){
                dp[j]=min(dp[j],dp[j-v[i]]+w[i]);
        }
                
    }
    //从最大值遍历,找到的第一个满足条件的重量即为答案
    for(int i=sum;i>=0;i--){
        if(dp[i]<=W){
            cout<<i<<endl;
            return 0;
        }
    }

}

2.完全背包问题

一件物品可以选很多次

3.多重背包问题

每个物品选择上限不同

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=100;

int n,m;
int f[N];

int main()
{
	cin>>n>>m;
	for(int i=0;i<n;i++){
		int v,w,s;
		cin>>v>>w>>s;
		for(int j=m;j>=0;j--){
			for(int k=1;k<=s&&k*v[i]<=j;k++)
				f[j]=max(f[j],f[j=k*v[i]+k*w[i]]);
		}
	}
	cout<<f[m]<<endl;
}

4.混合背包问题

背包有很多种

5.二维费用的背包问题

体积,重量

6.分组背包问题

所有物品分成若干组,每组选一件

7.背包问题求方案数

8.求背包问题的方案

9.有依赖的背包问题

选一个物品必须选它的依赖物品

线性DP

最长上升子序列(LIS)

例:由6个数,分别是: 1 7 6 2 3 4,求最长上升子序列。

评析:首先,我们要理解什么叫做最长上升子序列:1、最长上升子序列的元素不一定相邻 2、最长上升子序列一定是原序列的子集。所以这个例子中的LIS就是:1 2 3 4,共4个

1、n^2做法

首先我们要知道,对于每一个元素来说,最长上升子序列就是其本身。那我们便可以维护一个dp数组,使得
dp[i]表示以第i元素为结尾的最长上升子序列长度,那么对于每一个dp[i]而言,初始值即为1;

那么dp数组怎么求呢?我们可以对于每一个i,枚举在i之前的每一个元素j,然后对于每一个dp[j],如果元素i大于元素j,那么就可以考虑继承,而最优解的得出则是依靠对于每一个继承而来的dp值,取max.

int dp[i]//以第i个元素为结尾的最长上升子序列长度
int data[i]//存储元素
for(int i=1;i<=n;i++)
	{
		dp[i]=1;//初始化 
		for(int j=1;j<i;j++)//枚举i之前的每一个j 
		if(data[j]<data[i] && dp[i]<dp[j]+1)
		//用if判断是否可以拼凑成上升子序列,
		//并且判断当前状态是否优于之前枚举
		//过的所有状态,如果是,则↓ 
		dp[i]=dp[j]+1;//更新最优状态 
		
	}

dp[i]=dp[j]+1:i的值更新为 比第i个元素小的第一个元素j的最长子序列+1
最后,因为我们对于dp数组的定义是到i为止的最长上升子序列长度,所以我们最后对于整个序列,只需要输出dpn即可。
从这个题我们也不难看出,状态转移方程可以如此定义:

下一状态最优值=最优比较函数(已经记录的最优值,可以由先前状态得出的最优值)

2、nlogn 做法

我们其实不难看出,对于n2做法而言,其实就是暴力枚举:将每个状态都分别比较一遍。但其实有些没有必要的状态的枚举,导致浪费许多时间,当元素个数到了104-10^5以上时,就已经超时了。而此时,我们可以通过另一种动态规划的方式来降低时间复杂度:
将原来的dp数组的存储由数值换成该序列中,长度为i的上升子序列 的最小末尾数值

这其实就是一种几近贪心的思想:我们当前的上升子序列长度如果已经确定,那么如果这种长度的子序列的结尾元素越小,后面的元素就可以更方便地加入到这条我们臆测的、可作为结果、的上升子序列中。

int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		f[i]=0x7fffffff;
		//初始值要设为INF
		/*原因很简单,每遇到一个新的元素时,就跟已经记录的f数组当前所记录的最长
		上升子序列的末尾元素相比较:如果小于此元素,那么就不断向前找,直到找到
		一个刚好比它大的元素,替换;反之如果大于,么填到末尾元素的下一个q,INF
                就是为了方便向后替换*/ 
	}
	f[1]=a[1];
	int len=1;//通过记录f数组的有效位数,求得个数 
	/*因为上文中所提到我们有可能要不断向前寻找,
	所以可以采用二分查找的策略,这便是将时间复杂
    度降成nlogn级别的关键因素。*/ 
	for(int i=2;i<=n;i++)
	{
		int l=0,r=len,mid;
		if(a[i]>f[len])f[++len]=a[i];
		//如果刚好大于末尾,暂时向后顺次填充 
		else 
		{
            //当某个元素比f[]中最后一个元素小
		while(l<r)
		{	
            //二分查找,更新长度为len的最长上升子序列的最后一个元素的值
		    mid=(l+r)/2;
		    if(f[mid]>a[i])r=mid;
	    //如果仍然小于之前所记录的最小末尾,那么不断
	    //向前寻找(因为是最长上升子序列,所以f数组必然满足单调)
     
			else l=mid+1; 
		}
		f[l]=min(a[i],f[l]);//更新最小末尾 
     	}
    }
    cout<<len;
    ```

## 区间DP

## 计数类DP

## 数位统计DP

标签:背包,int,元素,DP,序列,相关,include,dp
来源: https://www.cnblogs.com/Jyming/p/16544804.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有