线性DP:经典题目

TZOJ:5752

循环遍历第一个字符串和第二个字符串,如果第一个字符串的第i个位置与第二个字符串的第j个位置是相同的,那么dp【i】【j】就是dp【i-1】【j-1】+1。如果不同就是从dp【i-1】【j】和dp【i】【j-1】中取最大,最后dp【n-1】【m-1】就是答案

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int m,n,dp[N][N];
string a,b;

signed main()
{
	cin>>a>>b;
	n=a.size();
	m=b.size();
	memset(dp,0,sizeof(dp));
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			if(a[i]==b[j])
			{
				dp[i+1][j+1]=dp[i][j]+1;
			}
			else dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
		}
	}
	cout<<dp[n][m]<<endl;
}

TZOJ:8542

因为子串必须要是连续的,所以整体做法和上面相同,只是在不同的时候要赋值0,而不是从dp【i-1】【j】和dp【i】【j-1】里选择一个较大值。

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int m,n,dp[N][N];
string a,b;

signed main()
{
	cin>>a>>b;
	n=a.size();
	m=b.size();
	memset(dp,0,sizeof(dp));
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			if(a[i]==b[j])dp[i+1][j+1]=dp[i][j]+1;
			else dp[i+1][j+1]=0;
		}
	}
	int Max=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)Max=max(Max,dp[i][j]);
	cout<<"max="<<Max<<endl;
}

TZOJ:1072

用一个二维dp数组记录编辑距离,还是循环遍历两个字符串,相同的时候编辑距离就是dp【i-1】【j-1】,不同的时候要从删除,插入和修改里选择一个最优的操作。

修改:dp【i-1】【j-1】+1

删除:dp【i-1】【j】+1和dp【i】【j-1】+1

插入:dp【i-1】【j】+1和dp【i】【j-1】+1

所以不同的时候就是从dp【i-1】【j-1】,dp【i-1】【j】+1和dp【i】【j-1】+1选择较小的值

最后输出dp【n-1】【m-1】即可。

初始化的时候遍历dp【i】【0】和dp【0】【j】的时候要初始化为i和j,因为一个空的字符串要和另一个字符串相同显然要插入另一个字符串长度的数量。

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+100;
string a,b;
int n,m,dp[N][N];
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	while(cin>>a>>b)
	{
		n=a.size();m=b.size();
		memset(dp,0,sizeof(dp));
		for(int i=0;i<=n;i++)dp[i][0]=i;
		for(int i=0;i<=m;i++)dp[0][i]=i;
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				if(a[i-1]==b[j-1])dp[i][j]=dp[i-1][j-1];
				else dp[i][j]=min(dp[i-1][j]+1,min(dp[i][j-1]+1,dp[i-1][j-1]+1));
			}
		}
		cout<<dp[n][m]<<endl;	
	}
}

TZOJ:8484

这题我把他看成青蛙跳台阶去偷,dp【i】用来记录到第i个房屋的时候能偷到的最大金额,偷到第j个房屋时,第j+1就不能偷,因此要去偷第j+2个,也可以直接偷第j+3个,这样就和前面那一种方法是不一样的。如果偷第j+4个,就不是最优的方法了,因为第j+4个是可以从第j+2个过来的,直接去第j+4个反而不是最优了,最后输出dp【n-1】和dp【n】的较优解即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+100;
int n,m,dp[N],a[N];
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	memset(dp,0, sizeof(dp));
	dp[1]=a[1];
	dp[2]=a[2];
	for(int i=3;i<=n;i++)
		dp[i]=max(dp[i-2],dp[i-3])+a[i];
	cout<<max(dp[n],dp[n-1])<<endl;
}

TZOJ:8500

由于每天只能持有一股股票,所以不能在每次低价的时候进行购买屯票再在高价的时候卖出,因此我要选择在第i天时对于第i天的利润最优的方法。

用dp【i】【0】和dp【i】【1】来表示第i天持有股票和未持有股票时的最大金额。

那么dp【i】【0】就可以从dp【i-1】【1】+a【i】和dp【i-1】【0】转换过来

dp【i】【1】从dp【i-1】【0】-a【i】和dp【i-1】【1】转换过来。

这样就做到了当前状态的最优,dp【n】【0】就是最终的答案。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,dp[N][5],a[N];
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	dp[1][0]=0;dp[1][1]=-a[1];
	for(int i=2;i<=n;i++)
	{
		dp[i][1]=max(dp[i-1][1],dp[i-1][0]-a[i]);
		dp[i][0]=max(dp[i-1][0],dp[i-1][1]+a[i]);
	}
	cout<<dp[n][0]<<endl;
}

TZOJ:8502

跟前一题一样,但是增加了买卖次数限制,意味着只能买k次。

那就让dp多一维,变成dp【i】【j】【0|1】表示在第i天进行了j次购买后,持有股票时和未持有股票时的最优利润。

dp【i】【j】【0】由dp【i-1】【j】【0】和dp【i-1】【j】【1】+a【i】转换。

dp【i】【j】【1】由dp【i-1】【j】【1】和dp【i-1】【j-1】【0】-a【i】转换。

dp【0】【j】【1】为非法状态,初始化为负的最大值。

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,a[N],k,dp[N][N][2];
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n>>k;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=0;i<=k;i++)dp[0][i][1]=-1e6;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=k;j++)
		{
			dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1]+a[i]);
			dp[i][j][1]=max(dp[i-1][j][1],dp[i-1][j-1][0]-a[i]);
		}
	}
	int Max=0;
	for(int i=0;i<=k;i++)Max=max(Max,dp[n][i][0]);
	cout<<Max<<endl;
}

TZOJ:8503

还是三维dp

dp【i】【0|1】【0|1】表示第i天是否有买卖冷却,是否持有股票。

那么dp【i】【0】【0】就由dp【i-1】【0】【0】和dp【i-1】【1】【0】转换。

dp【i】【1】【0】就由dp【i-1】【0】【1】+a【i】转换。

dp【i】【0】【1】就由dp【i-1】【0】【0】-a【i】以及dp【i-1】【0】【1】转换。

dp【i】【1】【1】由于表示的时候有冷却以及手上有票,但是有冷却的时候说明第i天已经卖出股票了,然后又把当天的股票买回来,算是非法的操作,所以就不用管。

初始化dp【0】【0】【0】为合法,初始化为0,dp【0】【0】【1】和dp【0】【1】【0】非法,初始化负的最大值。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,a[N],k,dp[N][2][2];
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	dp[0][0][1]=-1e6;
	dp[0][1][1]=-1e6;
	for(int i=1;i<=n;i++)
	{
		dp[i][0][0]=max(dp[i-1][0][0],dp[i-1][1][0]);
		dp[i][0][1]=max(dp[i-1][0][1],dp[i-1][0][0]-a[i]);
		dp[i][1][0]=dp[i-1][0][1]+a[i];
	}
	cout<<max(dp[n][1][0],dp[n][0][0])<<endl;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值