动态规划(一)

多阶段决策过程的最优化问题

重要概念

·阶段

·描述状态

·状态转移方程

线性模型

例1 OJ1258 数字金字塔


 

【分析】

根据样例找出规律即可

当前位置的数累加上下面两个位置中的最大值

【代码】

#include <bits/stdc++.h>
using namespace std;
int a[1005][1005];
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=i;j++){//注意是小于等于i 
			cin>>a[i][j];
		}
	}//输入金字塔 
	for(int i=2;i<=n;i++){//从第二行开始遍历 
		for(int j=1;j<=i;j++){
			a[i][j]+=max(a[i-1][j-1],a[i-1][j]); //规律qwq 
		}
	}
	int k=0;
	for(int i=1;i<=n;i++){
		if(a[n][i]>k) k=a[n][i];//输出最大值 
	}
	cout<<k;
	return 0;
}

例2 OJ1259 最长不下降序列

 【分析】

好吧我也不知道说什么了,详见代码。

打表!!!首先初始化l和suf,然后遍历,比较大小,你赋给我我赋给你

(数列中的数是可以相等的)

【代码】

#include <bits/stdc++.h> 
using namespace std;
struct S{
	int x,l,suf;//x表示数本身,l表示序列长度,suf表示后缀 
}a[205];
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i].x;//输入数本身 
		a[i].l=1;
		a[i].suf=-1;//令初始序列长度为1(数本身),后缀为-1,即无后缀 
	}
	for(int i=n-1;i>=1;i--){//从后往前看 
		int hi,hx;//hi用来标记后缀下标,hx用来标记后缀长度 
		hx=0;//假设后缀初始长度为0 
		for(int j=i+1;j<=n;j++){//
			if(a[j].x>=a[i].x && a[j].l>hx){//如果后面有一个数大于当前数(a[i].x),且该数的后面的序列长度大于0 
				hx=a[j].l;//将其序列长度赋给hx 
				hi=j;//并将该数的下标赋给hi 
			}
		}
		if(hx!=0){//不等于0说明上述for循环内容成立 
			a[i].l=hx+1;//a[i].l会等于上一个数的序列长度+1,a[i].x成为下一个数 
			a[i].suf=hi;//a[i].suf由j而来,则将hi赋值 
		}
	}
	int maxi,maxn=0;//maxi用来标记下标,maxn用来标记数 
	for(int i=1;i<=n;i++){//从1到n 
		if(a[i].l>maxn){
			maxn=a[i].l;
			maxi=i;//和上面类似 从而得到序列元素最大数 
		}
	}
	cout<<"max="<<maxn<<endl;//注意输出格式 
	int k;
	k=maxi;//k用来表示后缀下标 
	while(k!=-1){//有后缀时 
			cout<<a[k].x<<" ";//输出该下标对应的数 
			k=a[k].suf;//又令k等于该数对应的后缀 即成为下一个被输出数的下标 
	}
	return 0;
}

例3 OJ1260 导弹拦截

【分析】

嗯 

【代码】

#include <bits/stdc++.h>
#define N 100010
using namespace std;
int a[N],f[N];//a为输入的导弹高度,f用来存储拦截系统的最高高度 
int main(){
	int n=1;
	while(scanf("%d",&a[n])!=EOF) n++;//逐个读入导弹高度 
	n--;//遍历 
	f[0]=5000001;//设置第一个拦截系统的第一发能达到任意高度 
	int k=0;//还没有开始拦截 
	int i;
	for(i=1;i<=n;i++){
		if(f[k]>=a[i]){//如果当前导弹高度小于该系统当前的最高高度 
			f[k+1]=a[i];//实时更新最高高度即可 
			k++;
		}
		else{//如果大于 进行二分 找到第一个比它小的位置
			int l,r,mid;
			l=1,r=k;//注意是从1到k 
			while(l<r){
				mid=(l+r)/2;
				if(f[mid]>=a[i]) l=mid+1;//当前导弹高度小于f[mid] 即在右边 
				else r=mid;//大于f[mid]  即在左边 
			}
			f[l]=a[i];//更新 
		}
	}
	cout<<k<<endl;//输出能拦截的导弹数 
	memset(f,-1,sizeof(f));//每次循环初始化为-1 
	int m=0;//刚开始系统数为0 
	f[0]=-1;//嗯 
	for(i=1;i<=n;i++){
		if(f[m]<a[i]){ 
			f[m+1]=a[i];
			m++;
		}//如果当前导弹高度大于系统最高高度 需要重新配置系统m++ 并更新最高高度 
		else{//如果小于等于 
			int z;
			z=lower_bound(f,f+m+1,a[i])-f;//从f到f+m+1中寻找大于等于a[i]的数(得到地址),减去f即得到下标 
			f[z]=a[i];//更新 
		}
	}
	cout<<m;
	return 0;
}

【拓展】

lower_bound() 函数用于在指定区域内查找不小于目标值的第一个元素,定义在<algorithm>头文件中。也就是说,使用该函数在指定范围内查找某个目标值时,最终查找到的不一定是和目标值相等的元素,还可能是比目标值大的元素。

格式: lower_bound( first , last , a )

其中a为目标值,具体见代码。

例4 OJ261 城市交通路网​​​​​​​

【代码】

#include <bits/stdc++.h>
using namespace std;
const int N=15;
int a[N][N],b[N],f[N];//a为交通网,b用来记录最省交通费,f用来记录路径 
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>a[i][j];
		}
	}
	memset(b,0x3f,sizeof(b));//初始化 
	b[n]=0;//刚开始为0 
	for(int i=n;i>=1;i--){//从后往前推 
		for(int j=i+1;j<=n;j++){
			if(a[i][j]&&b[i]>b[j]+a[i][j]){ 
				b[i]=b[j]+a[i][j];
				f[i]=j;//比较得出最省费用,再将当前城市存入f 
			}
		}
	}
	cout<<"minlong="<<b[1]<<endl;//输出费用 
	for(int i=1;i!=n;i=f[i]) cout<<i<<" ";
	cout<<n;//打印路径 
	return 0;
}

 

练习 OJ284 摘花生

【分析】

1.运用01背包(或许吧)

2.比较最后从上面或是从左边来的数的大小,得到最大数。

【代码】

#include <bits/stdc++.h>
using namespace std;
int a[105][105],b[105][105];//a为矩形花生地,b用来计数
int r,c;
int main(){
	int T;
	cin>>T;
	while(T--){
		cin>>r>>c;
		for(int i=1;i<=r;i++){
			for(int j=1;j<=c;j++){
				cin>>a[i][j];
			}
		}
		for(int i=1;i<=r;i++){
			for(int j=1;j<=c;j++){
				b[i][j]=max(b[i][j-1],b[i-1][j])+a[i][j];//比较取最大值
			}
		}
		cout<<b[r][c]<<endl;
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值