入门基础-动态规划

https://www.luogu.com.cn/problem/P1060

P1060 [NOIP2006 普及组] 开心的金明

一道典型的背包问题

思路:先说二维的dp[i][j],i表示0---i件物品是花费j元的情况,也就是说先从第一层(i=1)的情况都更新,然后再更新(i=2)的情况,i=2的情况是在i=1的情况下推倒出来的,因此我们就可以得到

一维转移方程:f[j]=max(f[j],f[j-v[i]]+v[i]*w[i])

dfs:思路,选或者不选,每个都枚举一遍,要注意最后意见物品的选择(如果我们选择最后一件物品,那么number会出现number>m的情况,因此我们要在判断位置之前要更新我们的答案)以及花钱的边界。

dfs:

#include<bits/stdc++.h>
#define ll long long
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define hh 0x3f3f3f3f
using namespace std;
inline int read()
{
	ll s=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<3)+(s<<1)+c-'0';c=getchar();}
	return s*f;
}
ll value[3000005],spend[3000005],n,m;
int ans=0;
void dfs(int number,int money,int jiazhi)
{
	if(money>0)ans=max(jiazhi,ans);
	if(number>m)return;
	dfs(number+1,money-spend[number],jiazhi+value[number]);//选 
	dfs(number+1,money,jiazhi);//不选 
}
int main()
{
	ios::sync_with_stdio(0);cout.tie(0);cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>spend[i]>>value[i];
		value[i]=spend[i]*value[i];
	}
	dfs(1,n,0);
	cout<<ans<<endl;
	return 0;
}

二维代码:转移方程 dp[i][j]=max(dp[i-1][j],dp[i-1][j-spen[i]]+value[i]);

#include<bits/stdc++.h>
#define ll long long
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define hh 0x3f3f3f3f
using namespace std;
inline int read()
{
	ll s=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<3)+(s<<1)+c-'0';c=getchar();}
	return s*f;
}
ll dp[30][3000005],n,m,ans,value[3000005],spend[30000005];
int main()
{
	ios::sync_with_stdio(0);cout.tie(0);cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>spend[i]>>value[i];
		value[i]=spend[i]*value[i];
	}
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(j>=spend[i]) 
			{
				dp[i][j]=max(dp[i-1][j],dp[i-1][j-spend[i]]+value[i]);
			}
			else dp[i][j]=dp[i-1][j];
		}
	}
	cout<<dp[m][n]<<endl;
	return 0;
}

一维代码:一维转移方程:f[j]=max(f[j],f[j-v[i]]+v[i]*w[i])

#include<bits/stdc++.h>
#define ll long long
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define hh 0x3f3f3f3f
using namespace std;
inline int read()
{
	ll s=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<3)+(s<<1)+c-'0';c=getchar();}
	return s*f;
}
ll dp[3000005],n,m,ans,value[3000005],spend[30000005];
int main()
{
	ios::sync_with_stdio(0);cout.tie(0);cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>spend[i]>>value[i];
		value[i]=spend[i]*value[i];
	}
	for(int i=1;i<=m;i++)
	{
		for(int j=n;j>=spend[i];j--)
		{
		dp[j]=max(dp[j],dp[j-spend[i]]+value[i]);
		}
	}
	cout<<dp[n]<<endl;
	return 0;
}

https://www.luogu.com.cn/problem/P1164

P1164 小A点菜

dfs:普通的dfs的时间复杂度是:2^100,显然如果都枚举完会超时,那么我们就需要通过剪枝来优化我们的复杂度,就能过了。

dfs+剪枝代码:

#include<bits/stdc++.h>
#define ll long long
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define hh 0x3f3f3f3f
using namespace std;
inline int read()
{
	ll s=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<3)+(s<<1)+c-'0';c=getchar();}
	return s*f;
}
ll ans=0,n,m,spend[1005],dp[105][100005];
ll dfs(int i,int m)
{
	if(i>n||m<0)return 0;//边界 
	if(dp[i][m])return dp[i][m];//剪枝 
	if(m==spend[i])dp[i][m]+=1;//如果剩余的钱刚好够钱点第i个那么我们就让总的方案书+1 
	dp[i][m]+=dfs(i+1,m-spend[i]);//点第i个套餐 
	dp[i][m]+=dfs(i+1,m);//不点第i个套餐 
	//总方案数=点第i个套餐的方案+不点第i个套餐的方案,i从1开始,因此前面每个i的情况都枚举完了。 
	return dp[i][m];
}
int main()
{
	ios::sync_with_stdio(0);cout.tie(0);cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>spend[i];
	dp[n][m]=dfs(1,m);
	cout<<dp[n][m];
	return 0;
}

二维dp:转移方程:if(j>=spend[i]) dp[i][j]=dp[i-1][j]+dp[i-1][j-spend[i]];

                            else                     dp[i][j]=dp[i-1][j];

代码:

#include<bits/stdc++.h>
#define ll long long
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define hh 0x3f3f3f3f
using namespace std;
inline int read()
{
	ll s=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<3)+(s<<1)+c-'0';c=getchar();}
	return s*f;
}
ll dp[105][10005],n,m,spend[10005]; 
int main()
{
	ios::sync_with_stdio(0);cout.tie(0);cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>spend[i];
	for(int i=0;i<=n;i++)dp[i][0]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(j<spend[i])dp[i][j]=dp[i-1][j];
			else dp[i][j]=dp[i-1][j]+dp[i-1][j-spend[i]];
		}
	}
	cout<<dp[n][m]<<endl;
	return 0;
}

一维:dp[0]=1, dp[j]=dp[j]+dp[j-value[i]];

一维代码:

#include<bits/stdc++.h>
#define ll long long
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define hh 0x3f3f3f3f
using namespace std;
inline int read()
{
	ll s=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<3)+(s<<1)+c-'0';c=getchar();}
	return s*f;
}
ll dp[10005],n,m,spend[10005]; 
int main()
{
	ios::sync_with_stdio(0);cout.tie(0);cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>spend[i];
	dp[0]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=m;j>=spend[i];j--)
		{
			dp[j]=dp[j]+dp[j-spend[i]];
		}
	}
	cout<<dp[m]<<endl;
	return 0;
}

https://www.luogu.com.cn/problem/P1115

P1115 最大子段和

#include<bits/stdc++.h>
#define ll long long
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define hh 0x3f3f3f3f
using namespace std;
inline int read()
{
	ll s=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<3)+(s<<1)+c-'0';c=getchar();}
	return s*f;
}
int f[211111],n;
int main()
{
	ios::sync_with_stdio(0);cout.tie(0);cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int x;
		cin>>x;
		f[i]=max(f[i-1]+x,x);//dp
	}
	int maxx=-999999;
	for(int i=1;i<=n;i++)
	{
		maxx=max(f[i],maxx);
	}
	cout<<maxx<<endl;
	return 0;
}

https://www.luogu.com.cn/problem/P1734

P1734 最大约数和

dp题

一维转移方程:dp[j]=max{dp[j],dp[j-i]+yue[i]};

如果用二维来写,那么需要注意的是在第二个循环里,我们必须对每一个数都更新,因为在下一层状态可能需要到本层的状态,这里我被卡了好久...

二维转移方程:if(j<i)dp[i][j]=dp[i-1][j];

                         else      dp[i][j]=max{dp[i-1][j],dp[i-1][j-i]+yue[i]};

一维代码:

#include<bits/stdc++.h>
#define ll long long
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define hh 0x3f3f3f3f
using namespace std;
inline int read()
{
	ll s=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<3)+(s<<1)+c-'0';c=getchar();}
	return s*f;
}
int yueshu[1005],dp[1005],n;
int main()
{
	ios::sync_with_stdio(0);cout.tie(0);cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int temp=0;
		for(int j=1;j<=i/2;j++)
		{
			if(i%j==0)
			{
				temp+=j;
			}
		}
		yueshu[i]=temp;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=n;j>=i;j--)
		{
			dp[j]=max(dp[j],dp[j-i]+yueshu[i]);
		}
	}
	cout<<dp[n]<<endl;
	return 0;
}

二维代码:

#include<bits/stdc++.h>
#define ll long long
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define hh 0x3f3f3f3f
using namespace std;
inline int read()
{
	ll s=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<3)+(s<<1)+c-'0';c=getchar();}
	return s*f;
}
int yueshu[1005],dp[1005][1005],n;
int main()
{
	ios::sync_with_stdio(0);cout.tie(0);cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int temp=0;
		for(int j=1;j<=i/2;j++)
		{
			if(i%j==0)
			{
				temp+=j;
			}
		}
		yueshu[i]=temp;
	}
	for(int i=1;i<=n;i++)
	{
	    for(int j=1;j<=n;j++)
	    {
	    	if(j<i)dp[i][j]=dp[i-1][j];
	        else dp[i][j]=max(dp[i-1][j],dp[i-1][j-i]+yueshu[i]);
	    }
	}
	cout<<dp[n][n];
	return 0;
}

https://www.luogu.com.cn/problem/P1002

P1002 [NOIP2002 普及组] 过河卒

画图,转移方程:dp[i][j]=max(d[i][j],dp[i-1][j]+dp[i][j-1]);

#include<bits/stdc++.h>
#define ll long long
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define hh 0x3f3f3f3f
using namespace std;
inline int read()
{
	ll s=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<3)+(s<<1)+c-'0';c=getchar();}
	return s*f;
}
ll dp[25][25],pd[25][25],mx,my,zx,zy;
int dx[]={0,1,1,-1,-1,-2,-2,2,2};
int dy[]={0,2,-2,2,-2,1,-1,1,-1};
int main()
{
	ios::sync_with_stdio(0);cout.tie(0);cin.tie(0);
	cin>>zx>>zy>>mx>>my;
	zx=zx+2;zy=zy+2;mx=mx+2;my=my+2;
	for(int i=0;i<=8;i++)
	{
		int xx=mx+dx[i];
		int yy=my+dy[i];
		pd[xx][yy]=1;
	}
	dp[2][2]=1;
	for(int i=2;i<=zx;i++)
	{
		for(int j=2;j<=zy;j++)
		{
			if(pd[i][j])continue;
			else dp[i][j]=max(dp[i][j],dp[i-1][j]+dp[i][j-1]);
		}
	}
	cout<<dp[zx][zy]<<endl;
	return 0;
}

https://www.luogu.com.cn/problem/P1616

P1616 疯狂的采药

转移方程:dp[j]=max(dp[j],dp[j-spend[i]]+value[i]);

                        答案=max(选择,不选择);

#include<bits/stdc++.h>
#define ll long long
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define hh 0x3f3f3f3f
using namespace std;
inline int read()
{
	ll s=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<3)+(s<<1)+c-'0';c=getchar();}
	return s*f;
}
ll value[20000005],spend[20000005],dp[20000005],n,t;
int main()
{
	ios::sync_with_stdio(0);cout.tie(0);cin.tie(0);
	cin>>t>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>spend[i]>>value[i];
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=t;j++)
		{
			if(j>=spend[i])dp[j]=max(dp[j],dp[j-spend[i]]+value[i]);
		}
	}
	cout<<dp[t];
	return 0;
}

https://www.luogu.com.cn/problem/P1048

P1048 [NOIP2005 普及组] 采药

转移方程:dp[j]=max(dp[j],dp[j-spend[i]+value[i]);

#include<bits/stdc++.h>
#define ll long long
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define hh 0x3f3f3f3f
using namespace std;
inline int read()
{
	ll s=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<3)+(s<<1)+c-'0';c=getchar();}
	return s*f;
}
ll dp[100000],n,t,value[100000],spend[100000];
int main()
{
	ios::sync_with_stdio(0);cout.tie(0);cin.tie(0);
	cin>>t>>n;
	for(int i=1;i<=n;i++)cin>>spend[i]>>value[i];
	for(int i=1;i<=n;i++)
	{
		for(int j=t;j>=spend[i];j--)
		{
			dp[j]=max(dp[j],dp[j-spend[i]]+value[i]);
		}
	}
	cout<<dp[t];

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值