牛客周赛 Round 54

总结

 题目不是很难,D题bfs很久没用了,有些生疏,E题背包DP加了一些限制条件,希望下次遇到能写出来

A.清楚姐姐的糖葫芦

题意

找字符串中o的个数

思路

用string输入,用count函数查找

代码

void solve () {
	string s;cin>>s;
	int k=count(ALL(s),'o');
	cout<<k;
}

B.清楚姐姐买竹鼠

题意

a元买一只,b元买三只,问买x只至少花多少钱

思路

比较第一种和第二种哪个更便宜,最后再考虑多买几只b类的会不会比正好买a只便宜

代码

void solve () {
	int a,b,x;cin>>a>>b>>x;
	if (3*a<b) {
		cout<<a*x;
	}
	else {
		int pos=x%3;
		int q=x/3;
		cout<<min(q*b+pos*a,(q+1)*b);
	}
}

C.竹鼠饲养物语

题意

 相应的饲料会让竹鼠升级,例如1能让0等级的竹鼠升一级,2能让1等级的竹鼠升一级,但是3不能让1等级的竹鼠升级。给出一串序列,问最多能让多少个竹鼠升级,有无限多个0等级竹鼠

思路

 题目的m范围比较大,但是根本用不了那么多,直接就可以剪枝到n的大小。我们直接找1,2,3,4这种连续的序列,从n等于1开始遍历,相加每个的出现次数,但是如果3出现了3次,4出现了8次,想加的时候4只能加3,后面如果5,6,7的次数很大也只能加3,如果更小就要加更小的。因为没有饲料给他们升级了,只能按照小的加。

代码

void solve () {
	map<int,int>mp;
	int n,m;cin>>n>>m;
	for (int i=1;i<=n;i++) {
		int x;cin>>x;mp[x]++;
	}
	int q=mp[1],ans=0;
	for (int i=1;i<=n&&i<=m;i++) {
		if (mp[i]==0) break;
		if (i==1) {
			ans+=mp[1];continue;
		}
		ans+=min(mp[i],mp[i-1]);
		if(mp[i]>mp[i-1])
		mp[i]=mp[i-1];
	}
	cout<<ans;
}

D.清楚姐姐跳格子

题意

 刚开始清楚姐姐站在第一个格子上面,她的目标是到第n个格子上面。她能跳的长度取决于自己脚下格子的大小。能跳的长度是格子大小的正因子,问最少要几步能到达第n格

思路

 范围比较小,用bfs能直接过,这一题和上面一题差不多, a i a_i ai的范围虽然很大,但是我们格子最大才1e3,因此我们每次跳多少直接枚举到n就可以。
 这一题的bfs我没有A掉,赛后看别人的代码才明白,就是跳多少这一步很关键,写的很好。
用队列套pair来存储下标和跳的步数,用数组b来存储跳到i格需要的最少步数。
后来枚举的过程中我们枚举i的值,当a[y]%abs(y-i)==0(y是下标)的时候,i这个位置就可以从y这个位置跳到。范围不大,时间复杂度不会超。

代码

const int N = 2e5+5;
int n;int a[N],b[N];

void bfs () {
	queue<PII>q;
	q.push({0,1});
	b[1]=0;
	fill (b,b+10000,10000);//这里用fill初始化一个最大值
	while (!q.empty()) {
		PII t=q.front();q.pop();
		int x=t.fi,y=t.se;
		if (b[y]<x) continue;//本来就是给b数组找最小值,如果我都比你小了,那就不用往下处理了
		for (int i=1;i<=n;i++) {
			if (i==y) continue;
			if (a[y]%abs(y-i)==0&&x+1<b[i]) {
				b[i]=x+1;//这里跳了一步就再加1
				q.push({x+1,i});
			}
		}
	}
}

void solve () {
	cin>>n;
	for (int i=1;i<=n;i++) cin>>a[i];
	bfs ();
	cout<<b[n];
}

 x是最少多少步,b数组用来存储到i时最少多少步到达,y是下标。
很巧妙的一步就是abs(y-i)的一步,正好可以把左右两边的都处理完

E.清楚姐姐的布告规划

题意

给出n个数, a i a_i ai代表一块布的长度,要把这n块布张贴在长度为n的板子上面,并且每块布不能有重叠,可以紧凑起来,并且第i块布要覆盖第i块区域。

思路

这个题乍一看很难,实际上一点也不容易。

  • 主要就是第i块布要覆盖第i块区域。这道题的本质就是一道背包dp,但是我写了好久也没写出来,看了题解也没弄明白,就是这个第i块布要覆盖第i块区域。
  • 据我理解,当这块布在5这个位置的时候,假设长度为3,那么这块布的起始位置(因为布是有长度的)就是在3到5之间,那么尾部的位置就是5到7。
  • 有了这些我们就可以套用背包dp的板子了,下面看关键的状态转换过程
    dp[i]表示给前i部分放满最少用几张布
	fill(dp,dp+5050,5050);
	dp[0]=0;
	for (int i=1;i<=n;i++) {
		for (int j=min(i+a[i]-1,n);j>=i;j--) {
			if (j>=a[i])
			dp[j]=min(dp[j],dp[j-a[i]]+1);
		}
	}

 不同于背包dp的是我们的for (int j=min(i+a[i]-1,n);j>=i;j--),我们的这一步是不可或缺的,我们这样一来就可以确定这些都是符合题目规定的布,可以放置,所以我们后面就可以用01背包的模式用了。

代码

int a[5050],dp[5050];
void solve () {
	fill(dp,dp+5050,5050);
	int n;cin>>n;
	for (int i=1;i<=n;i++) {
		cin>>a[i];
	}
	dp[0]=0;
	for (int i=1;i<=n;i++) {
		for (int j=min(i+a[i]-1,n);j>=i;j--) {
			if (j>=a[i])
			dp[j]=min(dp[j],dp[j-a[i]]+1);
		}
	}
	if (dp[n]==5050) {
		cout<<"-1"<<'\n';
	}
	else cout<<dp[n]<<'\n';
}
  • 8
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值