zoukankan      html  css  js  c++  java
  • zhxのDP讲

    • zhxのDP讲

    DP基础例题

    1. 斐波那契数列

    f[1]=1;//第0项和第1项
    f[0]=0;
    
    //记忆化搜索
    int dp(int n)//斐波那契数列第n项
    {
    //g[i]表示f[i]有没有计算过
    if(n<=1)return n;
    if(g[n])return f[n];
    g[n]=true;
    f[n]=dp(n-1)+dp(n-2);
    return f[n];//O(fn)
    }
    
    
    //其它求当前
    for(int i=2;i<=a;i++)
    	f[i]=f[i-1]+f[i-2];
    //O(n)
    
    
    //当前求其它
    for(int i=0;i<=n;i++)
    {
    //O(n)
    f[i+1]+=f[i];
    f[i+2]+=f[i];
    }
    

    2. 组合数

    for (int i=0;i<=n;i++)
    {
    	C[i][0] = 1;//处理边界
    	for (int j=1;j<=i;j++)
    		C[i][j] = C[i-1][j-1] + C[i-1][j];
    }
    
    1. IOI数字三角形及其系列

    状态:f[位][置]=经过的数字之和

    f[i][j]:走到 (i,j) 的数字最大和

    1. 滑雪

    //滑雪
    n行m列
    可以走任意四个方向
    每走一步需要使得脚底数字变大
    最多走几个格子

    f[x][y]代表走到(x,y)的最长长度
    若:a[x-1][y]>a[x][y];
    f[x-1][y]=max(f[x-1][y],f[x][y]+1)
    
    //不正确解,使用自己求其他
    //但没有保证当前的位置求出
    cin  >> n >> m;
    for (int i=1;i<=n;i++)
    	for (int j=1;j<=m;j++)
    		cin >> a[i][j];
    
    for (int i=1;i<=n;i++)
    	for (int j=1;j<=m;j++)
    		f[i][j]=1;
    
    for (int x=1;x<=n;x++)
    	for (int y=1;y<=m;y++)
    		for (int d=上下左右)
    		{
    			int xx = x朝着d走;
    			int yy = y朝着d走;
    
    			if ( (xx,yy)存在 && a[xx][yy] > a[x][y]) f[xx][yy] = max(f[xx][yy],f[x][y]+1); 
    		}
    
    1. 最长上升子序列

    个人笔记
    https://www.cnblogs.com/frankchenfu/p/7107019.html

    1. 乌龟棋
    DP三要素:
    1. 状态
    2. 转移方程
    3. 边界条件

    DP可加维度

    f[i][j](二维)—f[i][j][k](三维)—f[i][j][k][l](四维)

    若时间空间过高,削减冗余状态,减少维度
    建一个和原来状态相同的数组储存方案或方案数

    有n*m的方格图

    DP模型

    背包DP

    01背包

    
    n个物品 背包容积为m 
    
    第i个物品体积为vi,价值为wi
    
    需要向背包里装一定的物品,在体积不超限的情况下装价值最高的物品
    

    f[编号][体积]=价值

    f[i][j]表示考虑完前i个物品 背包已经使用了j的体积

    //01背包
    cin>>n>>m;
    for(int i=1;i<=n;i++){
    cin>>v[i]>>w[i];
    }
    //v体积 w价值
    
    for(int i=1;i<=n;i++)
    	for(int j=0;j<=m;j++)
    	{
    		//考虑求f[i][j]
    		f[i][j]=f[i-1][j];//第i个物品不选
    		if(j>=v[i])		
    			f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i])
    	}
    

    例题采药

    无穷背包

    n种物品 背包容积为m
    每种物品数量不限
    第i个物品体积为vi,价值为wi
    f[i][j]表示考虑完前i种类物品 背包已经使用了j的体积

    //无穷背包
    
    cin>>n>>m;
    for(int i=1;i<=n;i++){
    cin>>v[i]>>w[i];
    }
    //v体积 w价值
    
    for(int i=1;i<=n;i++)
    	for(int j=0;j<=m;j++)
    	{
    		//考虑求f[i][j]
    		f[i][j]=f[i-1][j];//第i个物品不选
    		if(j>=v[i])		
    			f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i])
    	}
    
    例题疯狂的采药

    有限背包(多重背包)

    有N种物品和一个容量为T的背包,第i种物品最多有M[i]件可用,价值为P[i],体积为V[i]

    求解:选哪些物品放入背包,可以使得这些物品的价值最大,并且体积总和不超过背包容量。

    //有限(多重)背包
    cin>>n>>m;
    
    int k=0;
    //v体积 w价值 u个数 
    for(int i=1;i<=n;i++)
    {
    int v_,w_,x_;
    cin>>v_>>w_>>x_;
    int r=1;
    while(x_>=r)
    {
    	k++;
    	v[k]=v_*r;
    	w[k]=w_*r;
    	x_-=r;
    	r*=2;
    }
    if(x_!=0){
    k++;
    v[k]=v_*x_;
    w[k]=w_*x_;
    
    }
    
    }
    
    for(int i=1;i<=n;i++) 
    	for(int j=0;j<=m;j++) 
    		for(int k=0;k*v[i]<=j&&k<=u[i];k++){
    			//考虑如何求f[i][j] 
        f[i][j]=max(f[i][j],f[i-1][j-kv[i]]+k*w[i]);
    }
    
    

    区间DP

    区间DP特点:

    1. 状态设计前两维度一定为f[L][R];
    2. n<=100/200/500
    3. 只能对相邻的两个元素合并为一个
    4. 若区间为环形,将原数组在后面复制,组成长度为2n的数组,再进行区间DP
    5. 枚举分界点

    1. 石子合并

    f[l][r]表示区间[l,r]合并一堆的最小代价

    //石子合并
    //????[????][????]表示一段区间合并的最小代价
    //枚举断点合并
    cin>>n;
    for(int i=1;i<=n;i++){
    cin>>a[i];
    }
    
    for(int i=1;i<=n;i++)
    {
    
    sum[i]=sum[i-1]+a[i];
    f[i][i]=0;
    }
    for(int len=2;len<=n;len++)
    	for(int l=1,r=len;r<=n;l++,r++)
    			for(int k=l;k<r;k++)
    			f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+sum[r]-sum[l-1]);
    

    2.能量项链

    环形区间DP

    3.括号序列

    给出一个的只有()[]四种括号组成的字符串,
    求最多能够选出多少个括号满足完全匹配

    • 状态:为f[l][r] 代表区间[l,r]最多取出多少括号
    • 边界:f[i][i]=0;
    • 转移方程:

    4.回文子串

    求序列内有多少回文子串

    两个回文子串只要位置不同就算做不同的回文子串

    s[l]=s[r]

    s[l+1]=s[r-1]

    • 状态:f[l][r]表示l~r有多少回文子串

    求回文子序列

    • 状态:f[l][r]表示l~r有多少回文子序列

    关键问题在于去重

    转移一:容斥原理

    转移二:当左右字符相同时 再补充一次内部答案

    树形DP

    前置知识

    基于树的dp

    1. Dp方法始终为从下至上进行dp
    2. 在每个节点对所有儿子做聚合
    3. 可能需要多一遍dfs或者bfs
    • 状态:[i]表示以i为根的子树有多少个点
    • 边界条件以叶子节点来设置:f[叶子节点]=1;
    • 转移方程:将所有儿子的信息合并得到根节点信息f[i]=f[p1]+f[p2]+····+f[pk]+i;
    //树形DP
    void dfs(int i){
    	if(i is yezi)
    	{
    		f[i]=1;
    		return ;
    	}
    	for(p 是 i的 儿子)
    	{
    		dfs(p);
    		f[i]+=f[p];
    	}
    
    	f[i]++;
    
    }
    

    Problem 1

    询问树的最大独立集

    n个点的树,
    最多选几个点使得其互不相邻

    • 状态:f[i][j]

    i表示以i为根的子树最多选几个点

    j表示当前的i点选或不选(0/1)

    f[叶子][j]

    类似例题 没有上司的舞会

    Problem 2

    现在要在一棵树上布置士兵,每个sb在结点上,每个sb可以守护其结点直接相连的全部边,问最少需要布置多少个sb

    • 状态:f[i][j]以i为根的子树最少几个伞兵
      i表示以i为根的子树

    j取值为0/1/2

    • 0:i点放了伞兵

    • 1:i没放但是儿子放了

    • 2:i没有放并且儿子都没有放

    f[叶子][1]=无穷;
    f[叶子][2]=0;

    Problem 3

    求树上最远的两个点的距离(求树的直径)

    f[i][0]:i向下走最长的路

    f[i][1]:i向下走次长的路

    f[叶子][0/1]=0;

    f[i][0]=1+max(f[p][0],f[i][0]);

    Problem 4

    树形01背包(依赖背包)

    n个物品,背包容积为m,第i个物品体积为vi价值为wi,n个物品构成树,i进入背包前需要保证父亲已经进入背包

    f[i][j]以i为根的子树,已经用了j的体积,能获得的最大价值之和

    金明的预算方案

    排列DP

    前置

    对于某个n!种排列中满足某个条件的排列有多少种
    需要把1~n这些数按照顺序(小到大/大到小)顺序插入进去

    Problem 1

    1~n排列中有多少个排列的逆序对的数量是偶数

    方程:
    f[i+1][j+i-k]+=f[i][j];

    但是!
    此题可以不用DP

    ans为(frac{n!}{2})

    Problem 2

    对于一个序列,定义其“激动值”为序列中严格大于前面所有数的
    元素的个 数。比如,{1,1,5,6,5}的激动值为3。 给定n个数
    p1,p2,...,pn,求这n个数的所有排列中,激动值不超过k的个数。
    1 ≤ k ≤ n ≤ 200,1 ≤ pi ≤ 200

    f[i][j]表示插入到i激动值为j的方案数

    Problem 3

    1~n的排列中,有多少最长上升子序列长度≤2

    Problem 4

    N个字符串,由数字组成,有n!种方法把它们连在一起,能得到n!个数,n!个数中有多少是11的倍。
    ∵奇位和-偶位和=11的倍数
    ∴这个数为11的倍数

    数位DP

  • 相关阅读:
    洗礼灵魂,修炼python(48)--巩固篇—模块
    洗礼灵魂,修炼python(48)--巩固篇—模块
    洗礼灵魂,修炼python(48)--巩固篇—模块
    Excel中拆分列
    Excel中拆分列
    Excel中拆分列
    Excel中拆分列
    Eclipse新建类的时候如何自动添加注释(作者,时间,版本等信息)
    Eclipse新建类的时候如何自动添加注释(作者,时间,版本等信息)
    用golang实现DDOS攻击网站
  • 原文地址:https://www.cnblogs.com/DAIANZE/p/15024698.html
Copyright © 2011-2022 走看看