动态规划【dp】

动态规划

  • 状态:

  • 状态转移方程:子问题的转移【下一步的决策】

  • 递归【留意顺序】【利用多重循环】

  • 记忆化搜索[dfs]【用数组记录值】

  • 防止爆内存: 滚动数组【一层一层更新】

DAG dp【有向无环图】->起点&&终点

写完整程序:如何输出【无解的标记】

嵌套矩形问题【最长路】
  • 建图【二元关系】

  • 从第几个点开始

    d[i]={max(d[j])+1};

    • 记忆化搜索
      • 起点为一
    int dp(int i)
    {
    	if(d[i]>0) return d[i];
    	d[i]=1;
    	for(int j=1;j<=n;j++)
    	{
    		if(G[i][j]==1)
    		{
    			d[i]=max(d[i],dp[j]+1);
    		}
    	}
    	return d[i];
    }
    
    • 递推
      • 利用cmp&struct先进行排序
      • 按顺序递归
    for(int i=1;i<=n;i++)
    	{
    		for(int j=1;j<=n;j++)//cmp排序之后是i-1,未排序就是n
    		{
    			if(G[i][j])
    			d[i]=max(d[i],d[j]+1);
    		}
    	}
    
    • 按字典序输出
硬币问题
  • 状态:还需要凑足的面值

  • 固定起点0 和终点 S

    • 递归

      for(int i=1;i<=s;i++)
      	{
      		for(int j=1;j<=n;j++)
      		{
      			if(i>=v[j])
      			{
      				min[i]=min(min[i],m[i-v[j]]+1);
      			}
      		}
      	}
      
      最长上升子序列
      • 状态:以i为结尾的最长子序列
      #include<bits/stdc++.h>
      using namespace std;
      int main()
      {
      	int n;
      	int a[6000];
      	cin>>n;
      	int d[6000];
      	for(int i=1;i<=n;i++)
      	{
      		cin>>a[i];
      		d[i]=1; 
      	}
      	
      	int ma=-1; 
      		for(int i=1;i<=n;i++)
      	{
      		for(int j=1;j<=i-1;j++)
      		{
      			if(a[j]<a[i])
      			d[i]=max(d[i],d[j]+1);//状态转移方程
      		}
      	}
      	//不确定最大的时候最大值的出现位置
      	for(int i=1;i<=n;i++)
      	{
      		if(ma<d[i])
      		ma=d[i];
      	}
      	cout<<ma;
      	return 0;
       } 
      
      LCS
      • 状态:两个长度为i和j的字符串的最大公共字符串长度
      • 难点: 如何输出结果
        • 类似于回溯:从末结点出发找到符合的道路
      #include<bits/stdc++.h>
      using namespace std;
      int d[3005][3005]={0};
      	char ans[10050];
      	char s[10050],t[10050];
      int main()
      {
      
      	scanf("%s%s",s+1,t+1);
      	//状态 : 两个长度为i,j的最大公共zixulie
      	//状态转移方程
      	int k=strlen(s+1);
      	int l=strlen(t+1);
      
      	for(int i=1;i<=k;i++)
      	{
      		for(int j=1;j<=l;j++)
      		{
      			if(s[i]==t[j])
      			{
      				if(i==0||j==0) d[i][j]=1;
      				else d[i][j]=d[i-1][j-1]+1;
      			}
      			else
      			{
      				if(i==0) d[i][j]=0;
      				else d[i][j]=max(d[i-1][j],d[i][j-1]);
      			}
      		}
      	}
      	while(d[k][l]>0)
      	{
      		if(s[k]==t[l])
      		{
      			ans[d[k][l]]=s[k];
      			k--;l--;
      		}
      		else 
      		{
      			if(d[k-1][l]==d[k][l]) 
      			{k--;}
      			else 
      			{l--;}
      		}
      	}
      	for(int i=1;i<=strlen(ans+1);i++)
      	cout<<ans[i];
      }
      
    背包问题
    • 草药问题【01背包问题】

      #include<iostream>
      using namespace std;
      int main()
      {
      	int t,m;
      	cin>>t>>m;
      
      	int a[1005]={0},b[1005]={0};
      	long long f[1005]={0};
      	for(int i=1;i<=m;i++)
      	{
      		cin>>a[i]>>b[i];
      	}
      	for(int i=1;i<=m;i++)
      	{
      		for(int j=t;j>=a[i];j--)
      		{
      			f[j]=max(f[j],f[j-a[i]]+b[i]);
      		}
      	}
      	cout<<f[t];//最大值在最后
      	return 0;
      }
      
  • 状态

    • 储存

      • 二维数组:用前i个草药 背包容量为j->记忆化搜索【dp】类似于dfs的循环【return 数】

      • 滚动数组:用递归的话与草药无关,只需列举背包容量

    • 状态转移方程:f[j]=max(f[j],f[j-a[i]]+b[i])

    • 重点

      • 枚举的顺序

        第一层:枚举草药的个数

        第二层循环:

        • 对于滚动数组:更新的顺序从后往前

          完全背包问题:从前往后

    • 最大值出现的位置:

      • 二维数组:第i个草药【0…V】
      • 一维数组:dp[V]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值