动态规划模板题合集

4 篇文章 0 订阅

背包DP

01背包

原题链接
在这里插入图片描述
思路
用前i个物品,体积为j时的最大值

代码

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int v[N], w[N], f[N];
int main()
{
    int n, V;
    
    cin >> n >> V;
    for (int i = 1; i <= n; i ++ )
    {
        cin >> v[i] >> w[i];
        for (int j = V; j >= v[i]; j -- )
        {
            f[j] = max(f[j - v[i]] + w[i], f[j]);
        }
    }
    cout << f[V] <<endl;
    return 0;
}

完全背包

原题链接
在这里插入图片描述
思路
用前i个物品,体积为j时,的最大价值,j从小到大枚举,因为是在同一层,看体积为j时,用物品i的最大价值。
代码

//********简化版
// #include <iostream>
// #include <cstring>
// #include <algorithm>
// using namespace std;
// const int N = 1010;
// int v[N], w[N], f[N];
// int main()
// {
//     int n, V;
    
//     cin >> n >> V;
//     for (int i = 1; i <= n; i ++ )
//     {
//         cin >> v[i] >> w[i];
//         for (int j = v[i]; j <= V; j ++)//for (int j = 0; j <= V; j ++)
//         {    //f[j] = f[j];
//              //if(j >= v[i)
//             f[j] = max(f[j - v[i]] + w[i], f[j]);
//         }
//     }
//     cout << f[V] <<endl;
//     return 0;
// }
//***************朴素版
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, V, v[N], w[N], f[N][N];
int main()
{
    cin >> n >> V;
    for(int i = 1; i <= n; i ++)
    {
        cin >> v[i] >> w[i];
        for(int j = 0; j <= V; j ++)
        {
            f[i][j] = f[i - 1][j];//之前放不进去的时候就不放,就是i - 1 时的价值
            if(j >= v[i])//有可能不存在所以需要判断
            {
                f[i][j] = max(f[i][j], f[i][j-v[i]] + w[i]);//一一枚举物品,能往里加就往里加,同一个物品,i不减1意思是在同一层上
            }
            
        }
    }
    cout << f[n][V] << endl;
    return 0;
}

多重背包1

原题链接在这里插入图片描述
思路
前i个物品,体积为j,用k个物品i时的最大价值

代码

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110;
int v, w, s, f[N];//只能开一维,二维会错
int main()
{
    int n, w, V;
    cin >> n >> V;
    for (int i = 1; i <= n; i ++ )//枚举前i个
    {
        cin >> v >> w >> s;
        for (int j = V; j >= v; j -- )//01背包的扩展,所以从大到小
        {
            for (int k = 0; k <= s && v*k <= j; k ++ )//需要加一个v * s <= j,防止背包爆掉
            {
                f[j] = max(f[j], f[j - v * k] + w * k);
            }
        }
    }
    cout << f[V] << endl;
    return 0;
}

多重背包2(二进制优化版)

原题链接
在这里插入图片描述
思路
每个物品有自己的个数,那么就用2的1, 1 * 2, 1 * 2 * 2…为个数打包物品,也就是说每组有2的n次方个物品,剩下的物品也为一组,然后就变成了每种物品的个数有n组,然后就枚举每种物品以每组物品为种数,然后枚举体积,做01背包操作即可。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 25000;
int n, V, v[N], cnt, w[N], k, f[N];
int main()
{
	cin >> n >> V;
	
	for(int i = 1; i <=n ; i ++)
	{
		int a, b, c;
		
		cin >> a >> b >> c;//体积,价值,数量 
		
		int k = 1;//从一开始 
		
		while(k <= c)//一共只有c个 
		{
			cnt ++;
		
			v[cnt] = k * a;//k个为一组 
		
			w[cnt] = k * b;//k个为一组 
		
			c -= k; //-k 
		
			k *= 2;//二进制优化,所以乘二 
		}
		if(c > 0)//如果没有用完,剩余的也打包放进数组 
		{
			cnt ++;
		
			v[cnt] = c * a;
		
			w[cnt] = c * b;
		}
	}
	n = cnt;//一共有cnt个包 
	
	//开始01背包的操作 
	for(int i = 1; i <= n; i ++)
	{
		for(int j = V; j >= v[i]; j --)
		{
			f[j] = max(f[j], f[j - v[i]] + w[i]);
		}
	}
	cout << f[V] << endl;
	
	return 0;
}

二位费用背包

原题链接
在这里插入图片描述
思路
两个限制条件就变成二维数组,三种循环,前i个物品,体积不超过V,重量不超过M,的所有选法,取最大值。

代码

#include<bits/stdc++.h> 
using namespace std;
const int N = 1010;
int n, V, f[N][N], v, w, m, M;
int main()
{
	cin >> n >> V >> M;
	for(int i = 1; i <= n; i ++)
	{
		cin >> v >> m >> w;
		for(int j = V; j >= v; j --)//体积 
		{
			for(int k = M; k >= m; k --)//重量 
			{
				f[j][k] = max(f[j][k], f[j- v][k - m] + w);
			}
		}
	}
	cout << f[V][M] << endl;
	return 0;
}

分组背包

原题链接

在这里插入图片描述

思路
先输入,用二维数组存体积和价值 例: v [ 组别 ] [ 第几个] = 体积;

然后就用01背包的方法,三重循环,前i个物品,体积为j时,用第k个 的最大体积。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110;
int f[N], v[N][N], w[N][N], s[N], maxn = 0;
int n, V;
int main()
{
    cin >> n >> V;
    for(int i = 1; i <= n; i ++)
    {
        cin >> s[i];
        for (int j = 1; j <= s[i]; j ++ )
        {
            cin >> v[i][j] >> w[i][j];
        }
    }
    for(int i = 1; i <= n; i ++)
    {
        for(int j = V; j >= 0; j --)
        {
            for(int k = 0; k <= s[i]; k ++)
            {
                if(j >= v[i][k])
                {
                    f[j] = max(f[j], f[ j - v[i][k] ] + w[i][k] );
                }
                
            }
        }
    }
    cout << f[V] << endl;
    return 0;
}

线性DP

数字三角形

https://www.acwing.com/problem/content/900/

在这里插入图片描述
思路
每一层每个数和它上面的左右两个数取最大值,维护每一层每个点的最大值,最后一层找最大值就是最大路径。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
const int N = 1005;
signed main()
{
    int n, num[N][N]={0}, f[N][N] = {-0x3f3f3f};
    cin >> n;
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= i; j ++ )
        {
            cin >> num[i][j];
        }
    }
    for (int i = 0; i <= n; i ++ )
    {
        for (int j = 0; j <= n; j ++ )
        {
            f[i][j] = -0x3f3f3f;
        }
    }
    f[0][1]=0;
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= i; j ++ )
        {
            f[i][j] = max(f[i - 1][j] + num[i][j], f[i - 1][j - 1] + num[i][j]);
        }
    }
    int maxn = -0x3f3f3f3f;
    for (int i = 1; i <= n; i ++ )
    {
        maxn = max(f[n][i], maxn);
    }
    cout << maxn <<endl;
    return 0;
}

最长上升子序列

https://www.acwing.com/problem/content/897/
在这里插入图片描述

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1010;

int n;
int a[N], f[N];

int main()
{
	cin >> n;
	for(int i = 1; i <= n; i ++ )
	{
		cin >> a[i];
	}
	for(int i = 1; i <= n; i ++ )
	{
		f[i] = 1;//自己本身算一个 
		for(int j = 1; j < i; j ++ )
		{
			if(a[j] < a[i])//如果f[j]后面可以放f[i] ;
			{
				f[i] = max(f[j] + 1, f[i]);//是它本身现有的子序列大(有可能已经被赋过值)还是 当前的以f[j]结尾的子序列 
			}
		}
	}
	int ans = -0x3f3f3f3f;
	for(int i = 1; i <= n; i ++ )
	{
		ans = max(f[i], ans);
	} 
	cout << ans;
	return 0;
}

求最长子序列是什么(把子序列输出来)

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1010;

int n;
int a[N], f[N], g[N];

int main()
{
	cin >> n;
	for(int i = 1; i <= n; i ++ )
	{
		cin >> a[i];
	}
	for(int i = 1; i <= n; i ++ )
	{
		f[i] = 1;//自己本身算一个 
		for(int j = 1; j < i; j ++ )
		{
			if(a[j] < a[i])//如果f[j]后面可以放f[i] ;
			{
				//f[i] = max(f[j] + 1, f[i]);//是它本身现有的子序列大(有可能已经被赋过值)还是 当前的以f[j]结尾的子序列 
				if(f[i] < f[j] + 1)
				{
					f[i] = f[j] + 1;
					g[i] = j;//记录是从哪个状态转移来的 ,记下来的一定是最大的,因为到最大的,其他的就不会更新了 
					//以上一层值为下标,记录下一层的下标				
				}				
			}
		}
	}
	int ans = -0x3f3f3f3f, k = 1;
	for(int i = 1; i <= n; i ++ )
	{
		if(f[k] < f[i])
		{
			k = i;
		}
	} 
	cout << f[k];
	for(int i = 0 ; i < f[k]; i ++ )
	{
		cout << a[k] << " ";
		
		k = g[k];//一层一层向上找该子序列的上一个数 
	} 
	return 0;
}

最长公共子序列

https://www.acwing.com/problem/content/899/

在这里插入图片描述

思路在这里插入图片描述
代码

#include<bits/stdc++.h> 
using namespace std;
const int N = 1005;

int n, m;
char a[N], b[N];
int f[N][N];

int main()
{
	cin >> n >> m;
	scanf("%s %s", a + 1, b + 1);	
	for(int i = 1; i <= n; i ++ )
	{
		for(int j = 1; j <= m; j ++ )
		{
			f[i][j] = max (f[i - 1][j], f[i][j - 1]);
			if(a[i] == b[j])
			{
				f[i][j] = max(f[i - 1][j - 1] + 1, f[i][j]);
			}
		}
	}
	cout << f[n][m] << endl;
	return 0;
}

最短编辑距离

https://www.acwing.com/problem/content/904/

思路
这题其实还不是那么明白
//就是把三种情况都枚举一遍 ,然后每一步都取最小值记录一下,维护每一步都是最小值,枚举每个子串的情况,一步一步累加到最后一步

在这里插入图片描述
代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1010;

int n, m;
char a[N], b[N];
int f[N][N];

int main()
{
	scanf("%d%s", &n, a + 1);
	scanf("%d%s", &m, b + 1);
	
	for(int i = 0; i <= m; i ++ )
	{
		f[0][i] = i;
	}
	for(int i = 0; i <= n; i ++ )
	{
		f[i][0] = i;
	}
	
//就是把三种情况都枚举一遍 ,然后每一步都取最小值记录一下,维护每一步都是最小值,枚举每个子串的情况,一步一步累加到最后一步 
	for(int i = 1; i <= n; i ++ )
	{
		for(int j = 1; j <= m; j ++ )
		{
			f[i][j] = min(f[i - 1][j] + 1, f[i][ j - 1] + 1);
			if(a[i] == b[j])
			{
				f[i][j] = min(f[i][j], f[i - 1][j - 1]);
			}
			else
			{
				f[i][j] = min(f[i][j], f[i - 1][ j - 1 ] + 1);
			}
			//cout <<"i == "<< i << " j == "<<j<<"  "<< f[i][j] <<endl;
		} 
	}
	
	cout << f[n][m] << endl;
	return 0;
}

编辑距离

https://www.acwing.com/problem/content/description/901/

在这里插入图片描述
思路
同上 最短编辑距离

代码

#include<bits/stdc++.h> 
using namespace std;

int n, m, f[1005][1005];
char a[1005][1005];

int solved(char a[], char b[])
{
	int lena = strlen (a + 1);
	int lenb = strlen (b + 1);
	
	for(int i = 0; i <= lena; i ++ )
	{
		f[i][0] = i;
	}
	for(int i = 0; i <= lenb; i ++ )
	{
		f[0][i] = i;
	}
	
	for(int i = 1; i <= lena; i ++ )
	{
		for(int j = 1; j <= lenb; j ++ )
		{
			f[i][j] = min(f[i - 1][j] + 1, f[i][j - 1] + 1);
	
			if(a[i] == b[j])
			{
				f[i][j] = min(f[i - 1][j - 1], f[i][j]);
			} 
			else
			{
				f[i][j] = min(f[i - 1][j - 1] + 1, f[i][j]);
			}
		}
	}
	return f[lena][lenb];
} 
int main()
{
	
	cin >> n >> m;
	
	for(int i = 1; i <= n; i ++ )
	{
		scanf("%s", a[i] + 1);
	}
	while(m -- )
	{
		int flag = 0, ans = 0;
		char b[1005];
		
		scanf("%s%d", b + 1, &flag);
		
		for(int i = 1; i <= n; i ++ )
		{
			if(solved(a[i], b) <= flag )
			{
				ans ++ ;
			}	
		}
		cout << ans <<endl;
	}
	return 0;
}

区间DP

石子合并

https://www.acwing.com/problem/content/284/

在这里插入图片描述
思路
以最后一步进行分类,最后一步合并哪两个石子进行分类,枚举每个区间枚举每两部分石子,然后求MIN还不太懂,过几天取再复习一遍。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 310;
int n;
int s[N]; 
int f[N][N];

int main()
{
	cin >> n;
	for(int i = 1; i <= n; i ++ )
	{
		cin >> s[i];
		//s[i] = s[i - 1] + s[i];
	}
	
	for(int i = 1; i <= n; i ++ )
	{
		//cin >> s[i];
		s[i] += s[i - 1];
	}
	//memset(f, 999999, sizeof f);
	for(int len = 2; len <= n; len ++ )
	{
		for(int i = 1; i + len - 1 <= n; i ++ )
		{
			int l = i; int r = i + len - 1;
			f[l][r] = 1e9;
			for(int k = l ; k < r; k  ++ )
			{
				f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]);
			}
		}
	}
	cout << f[1][n] << endl;
	return 0;
}  
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值