2021寒假集训 Day1

7-6 题目描述:

n 个 人 要 住 房 , 两 人 间 a 元 一 间 , 三 人 间 b 元 一 间 , 问 最 少 花 费 ( 多 组 数 据 ) n个人要住房,两人间a元一间,三人间b元一间,问最少花费(多组数据) nab

题解:

很 容 易 想 到 贪 心 , 6 个 6 个 考 虑 会 出 很 多 问 题 , 如 剩 1 人 可 以 选 择 从 二 人 间 换 成 三 人 间 或 者 再 开 一 间 房 , 考 虑 的 情 况 实 际 上 和 直 接 贪 心 一 样 多 , 于 是 直 接 贪 心 : 很容易想到贪心,6个6个考虑会出很多问题,如剩1人可以选择从二人间换成三人间或者再开一间房,考虑的情况实际上和直接贪心一样多,于是直接贪心: 661

借 鉴 大 佬 的 代 码 借鉴大佬的代码

#include<bits/stdc++.h>
using namespace std;
int main(){
	int t;scanf("%d",&t);
	while(t--){
		long long n,a,b;
		cin>>n>>a>>b;
		if(n==1 || n==2) cout<<min(a,b)<<endl;
		else{
			if(a/2<b/3){
				if(n%2==0) cout<<n*a/2<<endl;
				else cout<<min((n/2+1)*a,(n/2-1)*a+b)<<endl;
			}
			else{
				if(n%3==0) cout<<n*b/3<<endl;
				else if(n%3==1) cout<<min(min((n/3+1)*b,(n/3)*b+a),(n/3-1)*b+2*a)<<endl;
				else if(n%3==2) cout<<min((n/3+1)*b,(n/3)*b+a)<<endl;
			}
		}
	}
	return 0;
}

7-7 题目描述

实 现 有 重 复 数 字 的 有 序 数 组 的 二 分 查 找 , 输 出 第 一 个 大 于 等 于 查 找 值 的 位 置 , 如 果 不 存 在 则 输 出 长 度 加 一 。 实现有重复数字的有序数组的二分查找,输出第一个大于等于查找值的位置,如果不存在则输出长度加一。

题解:

二 分 查 找 模 板 题 , 因 为 数 组 是 有 序 ( 单 调 不 减 ) 的 所 以 只 需 要 和 当 前 查 找 区 间 的 中 位 数 比 大 小 即 可 。 二分查找模板题,因为数组是有序(单调不减)的所以只需要和当前查找区间的中位数比大小即可。

题 面 很 清 楚 就 直 接 上 代 码 了 ,    变 量 名 也 改 成 了 很 好 理 解 的 版 本 。    题面很清楚就直接上代码了,~~变量名也改成了很好理解的版本。~~     

#include<bits/stdc++.h>
using namespace std;
int n,a[1000010];
int find(int v){
	if(v>a[n]) return n+1;
	int left=1;
	int right=n;
	int mid=(left+right)/2;
	while(left<right){
		if(a[mid]>=v) right=mid;
		else left=mid+1;
		mid=left+(right-left)/2;
	}
	return mid;
}
int main(){
	int k;
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	printf("%d",find(k));
	return 0;
}

此题还可以用STL中自带函数lower_bound()实现,接下来介绍原理:

l o w e r lower lower_ b o u n d ( b e g i n , e n d , n u m ) 能 够 利 用 二 分 查 找 找 到 从 b e g i n 到 e n d − 1 中 第 一 个 大 于 或 等 于 n u m 的 数 字 , 找 到 返 回 该 数 字 的 地 址 , 不 存 在 则 返 回 e n d 。 通 过 返 回 的 地 址 减 去 起 始 地 址 b e g i n , 得 到 找 到 数 字 在 数 组 中 的 下 标 。 bound(begin,end,num)能够利用二分查找找到从begin到end-1中第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。 bound(begin,end,num)beginend1numendbegin,

u p p e r upper upper_ b o u n d ( b e g i n , e n d , n u m ) : 从 数 组 的 b e g i n 位 置 到 e n d − 1 位 置 二 分 查 找 第 一 个 大 于 n u m 的 数 字 , 找 到 返 回 该 数 字 的 地 址 , 不 存 在 则 返 回 e n d 。 通 过 返 回 的 地 址 减 去 起 始 地 址 b e g i n , 得 到 找 到 数 字 在 数 组 中 的 下 标 。 bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。 bound(begin,end,num)beginend1numendbegin,

不 难 发 现 和 本 题 要 求 完 美 一 致 , 于 是 我 们 这 样 做 : 不难发现和本题要求完美一致,于是我们这样做:

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,k;
	int a[100010]={0};
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int ans=lower_bound(a+1,a+n+1,k)-a;//从1到n+1-1也就是n找到第一个大于等于k的数的位置 
	if(k>a[n] && ans==a[n]) printf("%d",n+1);
	else printf("%d",ans); 
	return 0;
}

注:因为写这种解法的时候题目已经被撤下,所以本代码并未经过准确程度检验。

7-8 题目描述

求 最 长 上 升 子 序 列 , 并 输 出 其 长 度 。 求最长上升子序列,并输出其长度。

题解:

最 长 上 升 子 序 列 ( L I S ) 的 模 板 题 , 属 于 学 习 线 性 动 态 规 划 ( D P ) 必 经 的 一 段 路 。 最长上升子序列(LIS)的模板题,属于学习线性动态规划(DP)必经的一段路。 LIS线DP

下 面 将 详 细 介 绍 L I S 的 基 本 思 路 : 下面将详细介绍LIS的基本思路: LIS

一 个 线 性 动 态 规 划 我 们 可 以 分 成 如 下 几 个 问 题 : 如 何 表 示 当 前 状 态 , 如 何 确 定 何 时 停 止 ( 边 界 ) , 如 何 确 定 转 移 方 程 以 及 最 终 的 目 标 。 一个线性动态规划我们可以分成如下几个问题:如何表示当前状态,如何确定何时停止(边界),如何确定转移方程以及最终的目标。 线

顺 着 这 个 思 路 来 看 一 下 L I S , 很 明 显 只 用 一 维 已 经 足 够 表 示 当 前 状 态 , 用 F [ i ] 表 示 以 n u m [ i ] 结 尾 的 L I S 的 长 度 。 顺着这个思路来看一下LIS,很明显只用一维已经足够表示当前状态,用F[i]表示以num[i]结尾的LIS的长度。 LISF[i]num[i]LIS

转 移 方 程 : F [ i ] = m a x ⏟ 0 ≤ j < i , A [ j ] < A [ i ] { F [ j ] + 1 } 转移方程:F[i]=\underbrace{max}_{0\leq j<i,A[j]<A[i]}\{F[j]+1\} F[i]=0j<i,A[j]<A[i] max{F[j]+1}

边 界 : F [ 0 ] = 0 边界:F[0]=0 F[0]=0

目 标 : m a x { F [ i ] } 目标:max\{F[i]\} max{F[i]}

于 是 只 要 找 到 一 个 比 当 前 的 数 更 大 的 数 就 能 够 让 当 前 的 序 列 长 度 加 一 , 这 样 理 解 起 来 没 有 任 何 思 维 难 度 , 但 唯 一 也 是 致 命 的 缺 点 是 太 慢 了 。 于是只要找到一个比当前的数更大的数就能够让当前的序列长度加一,这样理解起来没有任何思维难度,但唯一也是致命的缺点是太慢了。

这 种 做 法 的 时 间 复 杂 度 是 θ ( n 2 ) 的 , 在 数 据 范 围 稍 大 的 情 况 下 就 会 G G ( 但 是 过 本 题 足 矣 ) 。 这种做法的时间复杂度是\theta(n^2)的,在数据范围稍大的情况下就会GG(但是过本题足矣)。 θ(n2)GG

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,ans=1;
	int f[2][110]={0};
	int maxn=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int a;scanf("%d",&a);
		f[0][i]=a;
		f[1][i]=1;
	}
	
	for(int i=n-1;i>=1;i--){
		for(int j=i+1;j<=n;j++){
			if(f[0][j]>f[0][i] && f[1][j]>maxn){
				maxn=f[1][j];
			}
		}
		
		if(maxn==0) continue;
		f[1][i]=maxn+1;
	}
	for(int i=2;i<=n;i++) if(f[1][i]>f[1][ans]) ans=i;
	printf("%d",f[1][ans]);
	return 0;
}

上 面 的 代 码 是 能 够 顺 利 A C 的 , 有 兴 趣 的 可 以 继 续 往 下 看 优 化 部 分 。 上面的代码是能够顺利AC的,有兴趣的可以继续往下看优化部分。 AC

不 难 看 出 我 的 代 码 在 更 新 的 时 候 又 用 到 了 一 层 循 环 , 这 样 做 的 效 率 并 不 高 , 不难看出我的代码在更新的时候又用到了一层循环,这样做的效率并不高,

这 道 题 并 不 需 要 我 们 输 出 最 长 上 升 子 序 列 所 以 我 们 不 需 要 记 录 下 每 一 个 数 字 到 底 是 几 。 这道题并不需要我们输出最长上升子序列所以我们不需要记录下每一个数字到底是几。

模 拟 一 个 单 调 栈 , 栈 顶 元 素 就 是 我 们 刚 刚 加 进 去 的 序 列 的 最 后 一 个 元 素 。 因 为 这 个 栈 一 定 是 单 调 的 ( 要 找 上 升 序 列 所 以 一 定 是 单 调 的 ) 所 以 可 以 想 到 刚 刚 做 过 的 二 分 查 找 。 模拟一个单调栈,栈顶元素就是我们刚刚加进去的序列的最后一个元素。因为这个栈一定是单调的(要找上升序列所以一定是单调的)所以可以想到刚刚做过的二分查找。

如 果 这 个 数 比 栈 顶 还 大 那 当 然 要 加 入 , 但 是 如 果 这 个 数 比 栈 顶 小 但 是 也 比 前 面 的 一 个 数 小 , 我 们 就 能 够 把 这 个 数 替 换 掉 之 前 的 那 个 数 , 如果这个数比栈顶还大那当然要加入,但是如果这个数比栈顶小但是也比前面的一个数小,我们就能够把这个数替换掉之前的那个数,

这 样 虽 然 栈 里 的 元 素 不 能 构 成 传 统 意 义 上 的 上 升 序 列 但 也 能 保 证 数 字 的 单 调 。 同 时 也 为 后 来 可 能 出 现 的 比 目 前 栈 顶 元 素 小 的 数 提 供 了 加 入 的 可 能 。 这样虽然栈里的元素不能构成传统意义上的上升序列但也能保证数字的单调。同时也为后来可能出现的比目前栈顶元素小的数提供了加入的可能。

这 样 在 长 度 上 这 个 栈 并 没 有 变 化 ( 也 就 是 最 终 答 案 一 定 是 正 确 的 ) , 而 且 速 度 上 也 比 之 前 循 环 一 次 更 快 了 。 总 体 优 化 到 了 θ ( n l o g 2 n ) 。 这样在长度上这个栈并没有变化(也就是最终答案一定是正确的),而且速度上也比之前循环一次更快了。总体优化到了\theta(nlog_2n)。 θ(nlog2n)

以 上 优 化 部 分 推 荐 两 篇 博 客 以上优化部分推荐两篇博客 https://www.cnblogs.com/frankchenfu/p/7107019.html 和 和 https://www.cnblogs.com/wxjor/p/5524447.html

当 然 , 你 也 可 以 不 耻 下 问 来 找 我 ( 莫 棫 涵 ) 。 当然,你也可以不耻下问来找我(莫棫涵)。 ()

7-9 题目描述

一 段 序 列 每 次 询 问 一 段 区 间 的 和 。 一段序列每次询问一段区间的和。

题解:

前 缀 和 模 板 , 熟 练 掌 握 前 缀 和 的 思 想 能 够 在 预 处 理 时 更 加 得 心 应 手 。 前缀和模板,熟练掌握前缀和的思想能够在预处理时更加得心应手。

前 缀 和 的 思 想 并 不 难 , 假 设 原 数 组 为 A [ i ] 则 用 前 缀 和 数 组 B [ i ] 表 示 从 A [ 1 ] 到 A [ i ] 的 和 , 查 询 u ∼ v 之 间 的 和 直 接 用 B [ v ] 减 去 B [ u − 1 ] 即 可 。 这 样 做 在 多 次 查 询 时 直 接 把 复 杂 度 从 θ ( N K ) 变 成 了 θ ( N + K ) , 相 当 于 优 化 了 一 维 , 效 率 可 观 。 前缀和的思想并不难,假设原数组为A[i]则用前缀和数组B[i]表示从A[1]到A[i]的和,查询u\sim v之间的和直接用B[v]减去B[u-1]即可。这样做在多次查询时直接把复杂度从\theta(NK)变成了\theta(N+K),相当于优化了一维,效率可观。 A[i]B[i]A[1]A[i]uvB[v]B[u1]θ(NK)θ(N+K)

这 个 题 有 个 奇 怪 的 B u g 就 是 1 0 6 的 数 组 开 了 会 编 译 错 误 ( 段 错 误 ) , 于 是 你 们 等 会 看 到 我 代 码 中 一 种 神 奇 的 操 作 。 这个题有个奇怪的Bug就是10^6的数组开了会编译错误(段错误),于是你们等会看到我代码中一种神奇的操作。 Bug106

#include<bits/stdc++.h>
using namespace std;
int main() {
	int n,k;
	scanf("%d",&n);
	long long sum[n+10]={0};//我也不知道为什么可以这样,但就是可以这样(有更好解决方法的Dalao欢迎指教)
	 
	for(int i=1;i<=n;i++){
		int a;
		scanf("%d",&a);
		sum[i]=sum[i-1]+a;
	}
	
	scanf("%d",&k);
	
	for(int i=1;i<=k;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		printf("%d\n",(sum[v]-sum[u-1])%10000019);
	}
    return 0;
}

7-10 题目描述

卡 池 中 有 x 张 s 卡 和 y 张 a 卡 。 A 会 不 放 回 地 从 卡 池 中 抽 卡 , 而 B 会 在 A 每 次 抽 卡 前 猜 测 A 会 抽 到 什 么 。 卡池中有x张s卡和y张a卡。A会不放回地从卡池中抽卡,而B会在A每次抽卡前猜测A会抽到什么。 xsyaABAA

B 猜 测 是 根 据 卡 池 中 剩 余 的 总 量 s 卡 多 就 猜 s 卡 , a 卡 多 就 猜 a 卡 , 一 样 就 不 猜 , 问 最 后 B 猜 中 次 数 的 期 望 是 多 少 。 B猜测是根据卡池中剩余的总量s卡多就猜s卡,a卡多就猜a卡,一样就不猜,问最后B猜中次数的期望是多少。 BssaaB

题解:

看 到 数 学 题 我 就 打 起 了 表 , 手 玩 了 几 组 样 例 之 后 莽 地 输 出 了 两 者 中 更 大 的 数 , 发 现 对 了 . . 看到数学题我就打起了表,手玩了几组样例之后莽地输出了两者中更大的数,发现对了.. ..

接 下 来 证 明 这 个 结 论 : 设 E x ( a , b ) 为 所 求 期 望 ( 不 妨 设 a > b ) 接下来证明这个结论:设Ex(a,b)为所求期望(不妨设a>b) Ex(a,b)(a>b)

E x ( 1 , 1 ) = 1 ; Ex(1,1)=1; Ex(1,1)=1;

E x ( 1 , 2 ) = E x ( 2 , 1 ) = 2 3 + 2 3 ∗ ( 0 + 1 ) + 1 3 ∗ ( 1 + 1 ) = 2 Ex(1,2)=Ex(2,1)=\frac{2}{3}+\frac{2}{3}*(0+1)+\frac{1}{3}*(1+1)=2 Ex(1,2)=Ex(2,1)=32+32(0+1)+31(1+1)=2

⇒ E x ( a , b ) = a a + b + a a + b ∗ E x ( a − 1 , b ) + b a + b ∗ E x ( a , b − 1 ) \Rightarrow Ex(a,b)=\frac{a}{a+b}+\frac{a}{a+b}*Ex(a-1,b)+\frac{b}{a+b}*Ex(a,b-1) Ex(a,b)=a+ba+a+baEx(a1,b)+a+bbEx(a,b1)

⇒ E x ( a − 1 , b ) = a − 1 , E x ( a , b − 1 ) = b − 1 \Rightarrow Ex(a-1,b)=a-1,Ex(a,b-1)=b-1 Ex(a1,b)=a1,Ex(a,b1)=b1

⇒ E x ( a , b ) = a a + b + a a + b ∗ ( a − 1 ) + b a + b ∗ a = a ∗ ( a + b ) a + b = a \Rightarrow Ex(a,b)=\frac{a}{a+b}+\frac{a}{a+b}*(a-1)+\frac{b}{a+b}*a=\frac{a*(a+b)}{a+b}=a Ex(a,b)=a+ba+a+ba(a1)+a+bba=a+ba(a+b)=a

a < b 和 a = b 时 同 理 a<b和a=b时同理 a<ba=b

此 题 题 解 来 自 博 客 : 此题题解来自博客: :https://blog.csdn.net/hycfpl/article/details/108053333

#include<bits/stdc++.h>
using namespace std;
int main(){
	int a,b;
	scanf("%d%d",&a,&b);
	printf("%d.00",max(a,b));
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值