【算法每日一练]-动态规划 (保姆级教程 篇15)#动物 #赶deadline #page #构造字符串

news/2024/7/15 17:06:36 标签: 算法, c++, 数据结构, 深度优先, 图论, 动态规划

目录

今日知识点:

01背包的路径输出

计算位和的数位dp

不用管字符串,只需要看好约束dp转移的变量

动物

 赶deadline

page 

构造字符串 


        

        

动物

有某类动物,可以在农场待n天,每天最多增加一只动物,第i天到来的动物每天要吃的粮食为c[i],现在初始粮食是X,问在每天动物尽可能多的情况下最多容纳多少只动物?

输入:             输出:

3 4                        2

1 1 1

思路:

如果一直考虑每天的食量的话,这道题就不好做了。其实换个角度想一下:

动物来的时间是确定的,那么动物一共吃掉的食物也就确定了,那么者就转化成了01背包问题。

X是背包容量,每只动物的总食量是体积,价值就是1.

#include <bits/stdc++.h>
using namespace std;
int a[1005],f[100005],n,x;
int main(){
	cin>>n>>x;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		a[i]*=(n-i+1);
	}
	for(int i=1;i<=n;i++)
	for(int j=x;j>=a[i];j--)
		f[j]=max(f[j],f[j-a[i]]+1);
	cout<<f[x];
}

        

        

 赶deadline

输入:                                 输出:157
4 1000                                            2 3 4
50 500
75 400
60 300
22 200

         

 思路:

还是一道01背包。不过要输出路径,或者说输出拿到的物品。

举个例子把:

物品编号:       物品体积:       物品价值:

1 2 3 4              2 3 4 5              3 4 5 6

我们的背包转移情况如下图:

dp[i][j]=max(dp[i-1][j],dp[i-1][j-w]+v)

也就是说以蓝色格子为例:仅可以从正上方[1][3]或[1][0]转移过来(如图)。

那么也就是说那 如果f[2][3]和f[1][3]相等,说明没有拿第2个物品;否则就是拿了第2个物品,此时转移到f[1][3-w2]即f[1][0]。

        

 那么整个回溯过程如下面的草图。

每个格子[i][j]都只有两种选择要么是(不拿这个物品)向上走[i-1][j],要么是(拿这个物品) 走到f[i-1][j-w]。这样就能输出整个物品的装入情况了。

 

但是我的不是这么写的。因为是基于一维的背包。直接标记转移更新的就行,对于那些没有放入的物品,我们直接不去标记,这样也能直接找到回溯路径。(可以认为是上图中的取物品的为1,不取物品的为0)

       代码 部分:

#include <bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int t,n,w[N],v[N],f[N],pa[N][N],ans[N];
int main(){
	cin>>n>>t;
	for(int i=1;i<=n;i++)
		cin>>v[i]>>w[i];
	for(int i=1;i<=n;i++)
	for(int j=t;j>=w[i];j--)
		if(f[j-w[i]]+v[i]>f[j]){
			f[j]=f[j-w[i]]+v[i];
			pa[i][j]=1;
		}
	cout<<f[t]<<'\n';
	int i=n,j=t,cnt=0;
	while(i>=1&&j){
		if(pa[i][j]){
			ans[cnt++]=i;
			j-=w[i];
		}
		i--;
	}
	for(int k=cnt-1;k>=0;k--){
		cout<<ans[k]<<" ";
	}	
}

        

       

page 

思路:

一道数位dp题。(卡了我好久)
和之前一样,我最开始定义的是f[3][2]代表200~299的数字。

然后转移方程:f[i][j]=f[i-1][0~9]+j*pow(10,i-1),然后再求cal(n)的时候就发现不太好求了。

因为之前的cal过程是:

外层num[i]从高位到低位取数,里面0~num[i]-1全部加起来。然后逐渐发现不对劲……

最高位的数字一直在丢,没有加上去。导致结果不对。

最后,越写越丑,干脆不写了。

        

正确做法是可以直接的定义f[3][2]代表000~299的数字。

计算f[i][j]的转移方程是这样的:

举个例子:0~7999 即f[4][7]。

f[4][7]=7*10^3+f[4][9]+f[4][0]即:

0~7999=7000+0~6999+0~0999

故:f[i][j]=j*10^(i-1)+f[i][j-1]+f[i][0]

然后求cal(n):

求0~7213

0~7213=0~6999+7*214+0~213(对1000取模就行了)

0~213=0~199+2*14+0~13     (对100取模就行了)

0~13=0~9+1*4+0~3

外层num[i]从高位到低位取数,高位以下求和,然后计算高位数的次数,进入下循环。

终于结束了!!!!!!

        
感悟就是:初始化的转移方程最终求解cal(n)拆分方式的都至关重要。要亲自模拟一下。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
ll f[11][10];//f[i][j]表示数字长度为i且最大到以j开头的数字对应的答案数  eg:f[3][2]代表000~299的数字
void init(){
	for(int i=1;i<11;i++){
		f[i][0]=f[i-1][9];
		for(int j=1;j<=9;j++){
			f[i][j]=j*pow(10,i-1)+f[i][j-1]+f[i][0];
		}
	}
}


void cal(int x){
	vector<int>num;int y=x;
	while(x) num.push_back(x%10),x/=10;
	ll ans=0;
	for(int i=num.size()-1;i>=0;i--){
		int t=num[i];
		if(!t) continue;
		ans+=f[i+1][t-1]+(y%int(pow(10,i))+1)*t;
	}
	cout<<ans;
}
int main(){
	cin>>n;
	init();
	cal(n);
}

最后再说一下为什么这次换了这种定义方式:

因为这里不需要内这些内部的数进行处理。直接整体处理就行了。定义成f[i][j]表示i长度,j为最大开头的情况数; 但是之前的都是需要看开头数字的,所有只能定义成f[i][j]表示以i长度,j开始的数字的情况数。 

        

        

构造字符串 

用a,b,c来构造一个长n的字符串,要求:不能有连续三个相同的相邻,如aaa。问有多少种构造方法?

输入:
2
30
5000

思路:

初看还挺简单的,然后设置f[i][j]表示i长度,j结尾的方案数。然后还要考虑有几个相邻,开3维吗?

其实完全不用,a,b,c我们完全不需要管他们,只需要记录已经有几个相邻就行了。因为我们的转移仅仅受到相邻多少个相同的元素的约束。

那就直奔主题吧。设置f[i][1]表示长度i,结尾没有两个相同元素相邻;f[i][0]表示长度i,结尾有两个相同元素相邻;(哈哈哈,根本不存在有3个结尾相邻的情况,所以到0,1就足够了)

转移方程:f[i][0]=f[i-1][1]

f[i][1]=f[i-1][0]+f[f-1][1]

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll x,n,f[100005][2];
int main(){
	f[1][0]=0;f[1][1]=3;
	for(int i=2;i<=100000;i++){
		f[i][0]=f[i-1][1];
		f[i][1]=2*(f[i-1][0]+f[i-1][1])%mod;
	}
	cin>>x;
	while(x--){
		cin>>n;
		cout<<(f[n][0]+f[n][1])%mod<<'\n';
	}
}


http://www.niftyadmin.cn/n/5311097.html

相关文章

如何编写高效的正则表达式?

正则表达式&#xff08;Regular Expression&#xff0c;简称regex&#xff09;是一种强大的文本处理技术&#xff0c;广泛应用于各种编程语言和工具中。本文将从多个方面介绍正则表达式的原理、应用和实践&#xff0c;帮助你掌握这一关键技术。 正则可视化 | 一个覆盖广泛主题…

idea 以文本形式输出 SpringBoot项目 目录结构

第1步&#xff1a;AltF12 打开 Terminal 终端 第2步&#xff1a;cd 到 项目路径下 第3步&#xff1a;使用 tree 命令 结果 D:. ├─.mvn │ └─wrapper ├─applog │ └─logs ├─src │ ├─main │ │ ├─java │ │ │ └─com │ │ │ └─zhangziwa …

Android 编译过程介绍,Android.mk 和 Android.bp 分析, 在源码中编译 AndroidStudio 构建的 App

Android 编译过程介绍&#xff0c;Android.mk 和 Android.bp 分析&#xff0c; 在源码中编译 AndroidStudio 构建的 App_.mk编译目录所有.bp-CSDN博客

卫星时钟服务器、NTP时钟服务器、GPS北斗网络时钟系统

卫星时钟服务器、NTP时钟服务器、GPS北斗网络时钟系统 卫星时钟服务器、NTP时钟服务器、GPS北斗网络时钟系统 卫星时钟服务器、NTP时钟服务器、GPS北斗网络时钟系统 应用背景 根据人民银行第2012年第8期《金融业信息安全风险提示》建议大力推广采用能够接收GPS和北斗时钟源信号…

预训练模型的分类,以及代表模型介绍

预训练模型主要可以分为几个大的类型&#xff0c;这些类型通常基于它们所应用的任务和数据类型。以下是一些主要类型的预训练模型及其代表性模型&#xff1a; 自然语言处理&#xff08;NLP&#xff09;模型&#xff1a; 自回归语言模型&#xff1a;这类模型根据上文内容预测下一…

C++ 求一个数是否是丑数。

#include<string.h> #include <iostream> using namespace std; int isChou(int num) { if (num < 0) { return 0; } while (num % 2 0) { // 不断除以2&#xff0c;直到不能整除为止 num / 2; } while (num % 3 0) { // 不断除…

GMN双子座荣获抖音年度创意营销品牌奖 IP新营销成就睡养爆品

刚刚过去的2023新消费市场&#xff0c;有哪些优秀的新消费品牌行业表现抢眼?日前&#xff0c;在巨量引擎母婴宠物行业峰会上&#xff0c;GMN双子座 、美赞臣、合生元等大牌提名&#xff0c;并荣获抖音颁发的年度创意营销品牌奖,为行业变革之下&#xff0c;新消费品牌如何进行营…

[算法与数据结构][c++]:左值、右值、左值引用、右值引用和std::move()

左值、右值、左值引用、右值引用和std::move 1. 什么是左值、右值2. 什么是左值引用、右值引用3. **右值引用和std::move的应用场景**3.1 实现移动语义3.2 **实例&#xff1a;vector::push_back使用std::move提高性能** **4. 完美转发 std::forward**5. Reference 写在前面&…