CSDN 第 9 期题解

没啥时间写,所以写的比较草率。

而且不知道为什么最近发博客的 LaTeX \LaTeX LATEX 都渲染成很丑的样子了,CSDN 能不能修一下啊?

T1

题目大意

不好描述

代码

不太需要

T2

题目大意

求一个长度为 n n n 的序列中的最大连续子段和。

数据范围

忘了,但是开到 n ⩽ 1 0 5 n\leqslant 10^5 n105 绰绰有余。

题解

一眼 DP,不细说了,属于是真的没啥好讲的:

d p i = max ⁡ ( d p i − 1 + a i ) dp_{i}=\max(dp_{i-1}+a_i) dpi=max(dpi1+ai)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int dp[N],a[N],ans=0,n;
int main(){
	scanf("%d",&n);
	for(register int i=1;i<=n;i++) scanf("%d",&a[i]),dp[i]=max(0,a[i]);
	for(register int i=1;i<=n;i++){
		dp[i]=max(dp[i-1]+a[i],dp[i]);
	}
	for(register int i=1;i<=n;i++) ans=max(ans,dp[i]);
	printf("%d",ans);
}

T3

题目大意

有无限个 1 1 1 分, 2 2 2 分, 5 5 5 分, 10 10 10 分的硬币,你需要适当的组合他们,使得恰好取到 n n n 分钱,问有多少种本质不同的方案。

题解

因为本质不同了,所以交换两个硬币的顺序是没意义的,显然不能直接递推过去。

所以完全背包 DP 一下,由于物品只有四个,你可以拆成四个循环,反正就是先取若干 1 1 1 分钱,再取若干 2 2 2 分钱,以此类推。时间复杂度 O ( n ) \mathcal{O}(n) O(n)

非常需要注意的点

脑抽把完全背包写成多重背包了。

为什么 d p i = d p i − 1 + d p i − 2 + d p i − 5 + d p i − 10 dp_{i}=dp_{i-1}+dp_{i-2}+dp_{i-5}+dp_{i-10} dpi=dpi1+dpi2+dpi5+dpi10 是错的?注意到在这个状态转移方程下, d p 11 dp_{11} dp11 可能从 d p 10 dp_{10} dp10 推导过来,相当于先选择了一个 10 10 10 分硬币,再选择了一个 1 1 1 分硬币。而根据题目意思,硬币顺序不同,但是本质是相同的,所以这种解法已经被 d p 1 dp_1 dp1 重复计算了一遍,导致答案错误。

那为什么拆开这个循环,四次循环是对的,因为我限定他必须先选 1 1 1 分,再选 10 10 10 分,不可能会存在 d p 11 dp_{11} dp11 d p 10 dp_{10} dp10 推导过来导致重复计算的情况。这也是背包 DP 最核心的基础,强制要求一堆物品要按顺序选择。而这题明显是完全背包 DP 的模型,所以其本质的状态转移方程是:

d p i , j = d p i − 1 , j − 1 / 2 / 5 / 10 + 1 dp_{i,j}=dp_{i-1,j-1/2/5/10}+1 dpi,j=dpi1,j1/2/5/10+1

而由于我们通常会对 01 背包和完全背包 DP 进行滚动数组优化,所以我们省去了 i i i 的那一维,此时如果打的是 01 背包 DP,则需要将循环倒过来,防止一种物品被重复选择,但是完全背包 DP 就不用倒过来循环,因为本来就可以选择多次。

为了防止某些人误导萌新特意贴出来的一部分。

代码
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
const int N=1e5+5;
int dp[N],n;
int main(){
	scanf("%d",&n);
	dp[1]=1;
	for(register int i=1;i<=n;i++){
		if(i>=1) dp[i]=(dp[i]+dp[i-1])%mod;
	}
	dp[2]++;
	for(register int i=1;i<=n;i++){
		if(i>=2) dp[i]=(dp[i]+dp[i-2])%mod;
	}
	dp[5]++;
	for(register int i=1;i<=n;i++){
		if(i>=5) dp[i]=(dp[i]+dp[i-5])%mod;
	}
	dp[10]++;
	for(register int i=1;i<=n;i++){
		if(i>=10) dp[i]=(dp[i]+dp[i-10])%mod;
	}
	printf("%d",dp[n]);
}

T4

题目大意

不好描述,但是原题戳这 → \to CF148B

题解

不太需要,他说啥你做啥。稍微特判一下就行。

代码
#include<bits/stdc++.h>
using namespace std;
double vp,vd,t,f,c;
int main(){
	scanf("%lf %lf %lf %lf %lf",&vp,&vd,&t,&f,&c);
	if(vp>=vd){
		printf("0");
		return 0;
	}
	int ans=0;
	double now=vp*t;
	while(1){
		double res=now*1.0/(vd-vp);
		now+=vp*res;
		if(now-c>=0){
			printf("%d",ans);
			return 0;
		}
		now+=(res+f)*vp;
		ans++;
	}
	printf("%d",ans);
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值