总结
题目不是很难,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';
}