A | 自动收小麦机 |
题目描述
在游戏MC(我的世界)中,如果小麦碰到水流就会掉落,但是mc中的水不能无限流动,在同一高度一桶水往一个方向流最多可以流动8格(算上本身一格)。
但是如果流动过程中遇到了阶梯下降了高度,则从下降的那一格开始重新计算距离,所以根据这个原理可以设计一种自动收小麦机,只需要从最高处倒一桶水就可以把所有小麦变成掉落物。
这次小小航也根据这个原理建造了一个自动收小麦机,但是小小航并没有精确的计算台阶的位置,当小小航建造完后发现机器不能一次收集所有小麦,现在已知小小航的收小麦机总长为 n 格 ,水流可以流 k 格,每一格上的小麦数量为ai。
共有q次询问(每次询问之间互不影响),每次询问一个整数x,在第x格放一桶水共可以收获多少小麦。(水只会从高的一端流向低的一端)
输入描述:
第一行三个整数n,q,k(1≤n,q,k≤105)
第二行 n个数表示每格上小麦的数量(0≤数量≤109
第三行n个数表示每格的高度,保证非递减(0≤高度≤109
接下来q行 每行一个数x,表示在第x格放一桶水(1≤x≤n)
输出描述:
q行,每行一个整数,表示能收获的小麦数量。
示例1
输入
4 1 2 1 1 4 5 2 2 2 3 4
输出
10
说明
在第4格放出水流后,水流会流向第3格,由于第3格高度比第4格低,所以水流继续向左流向第2格,因为平地水流只能流2格,所以到达第2格后水流停止,收获的小麦数量为1 + 4 + 5 = 10
示例2
输入
5 2 2 1 1 4 5 1 2 2 3 3 4 4 3
输出
9 6
解题思路
先利用一个前缀和记录所有格子的前缀和,再利用一个数组储存每个格子所能收获的最大麦子量
代码实现
#include<iostream>
#include<unordered_map>
using namespace std;
const int N=1e5+10;
long long a[N],sum[N],h[N],f[N];
//a记录初始每格小麦的数量,sum记录初始时的所有前缀和
// h记录高度,f记录水在该点放后能收到的麦子
int main(){
int n,q,k;
cin>>n>>q>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
sum[i]=sum[i-1]+a[i];
}
unordered_map<int,int>s;
for(int i=1;i<=n;i++)
{
cin>>h[i];
if(s[h[i]]!=0)continue; //同一高度的格子的编号都为第一个格子的编号
else s[h[i]]=i;
}
for(int i=1;i<=n;i++){
//如果当前格子与前一个格子高度相同则进行判断是否超过k的范围
if(h[i]==h[i-1]){
//i-k+1是水在相同高度格子上流的最远距离
//相同高度的格子距离如果超过i-k+1那后面的麦子就收不了了
if(i-k+1>=s[h[i]])f[i]=sum[i]-sum[i-k];
else f[i]=sum[i]-sum[s[h[i]]-1]+f[s[h[i]]-1];
//f[i]为从这一个格子开始所有麦子减去前一个高度所有的麦子,最后加上前一个高度
//可以收获的麦子
}
else f[i]=f[i-1]+a[i];
//前一个格子能收的麦子加上这一个格子的麦子
}
while(q--){
int x;
cin>>x;
cout<<f[x]<<endl;
}
return 0;
}
D | 固执的RT |
题目描述(签到题)
RT你的帮助下取走了足够多的树枝,并用木制的马车拉着树枝去与人马决一死战。但他忘了一件事,人马是会喷火的。在战斗进行到一半时,卑鄙的人马用火焰将RT所有树枝烧光了。一般人可能已经放弃了,但固执的RT不愿意放弃。他决定再去收集树枝,和人马进行第二次战斗。
RT现在收集了n个树枝,第i个树枝的攻击力为ai。RT如果想打败人马,收集到的树枝攻击力之和至少为m。请你帮RT判断他现在收集到的树枝是否足够打败人马。
如果可以请输出“YES”, 否则输出“NO”(输出不带引号)。
输入描述:
第一行两个整数n,m。分别是树枝的数量和RT需要收集的树枝攻击力之和的最低要求。(1≤n≤1e5, 1≤m≤1e9)
第二行n个正整数,代表n个树枝的攻击力。(1≤ai≤1e9)
输出描述:
一行,如果RT收集到的树枝可以击败人马,输出“YES”,否则输出“NO”
示例1
输入
5 9 3 6 8 7 4
输出
YES
示例2
输入
5 9 1 1 1 1 1
输出
NO
#include <iostream>
using namespace std;
const int N=1e5+10;
int a[N];
long long sum;
int main() {
int n,m;
cin>>n>>m;
sum=0;
for(int i=0;i<n;i++){
cin>>a[i];
sum+=a[i];
if(sum>=m){
cout<<"YES"<<endl;
return 0;
}
}
cout<<"NO"<<endl;
return 0;
}
E | 释怀的RT |
题目描述
又一次收集完树枝后,RT做了防火工作,这次RT成功的使用了树枝战胜了人马。经过了长时间的痛苦折磨,RT释怀了并决定去海拉卢达陆上狩猎岩石巨人,用岩石巨人的心岩照亮心形湖来祭奠他还未开始就结束的爱情。
假设心形湖由1×\times×n个方格构成,RT在每个方格上放了一个心岩,每个心岩有一个照亮范围x,代表着这块心岩可以照亮它左边x个方格和右边x个方格,但不能照亮心岩所在的方格(假如一个心岩在第5个方格,x=2,那么他只能照亮第3,4和第6,7个方格),现在请你求出心形湖有多少个方格被照亮。
输入描述:
第一行一个正整数n,心形湖格子的个数。( 1 ≤n ≤ 1e6)
第二行n个整数,第i个数表示第i个心岩能照亮的范围。(0 ≤xi≤ 1e9)
输出描述:
一行,一个整数,表示照亮的格子数
示例1
输入
5 0 1 0 0 10
输出
4
说明
前四个方格被最后一个心岩照亮
示例2
输入
0 1 0 0 1
输出
3
说明
第一个方格和第三个方格被第二个格子的心岩照亮,第四个方格被第五个格子的心岩照亮,一共有三个格子被照亮
解题思路
开两个数组,对初始数组正反各遍历一遍,把所有能照亮的a[i]都用bool数组进行标记,最后统计标记的数量,输出
代码实现
#include<iostream>
using namespace std;
const int N=1e6+10;
int a[N];
bool s[N];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=0;i<=n;i++){
if(a[i]){
int mid=a[i];
while(mid--)
{
i++;
if(i>n)break;
s[i]=true;
if(a[i]>mid)mid=a[i];
}
}
}
for(int i=n;i>=1;i--){
if(a[i]){
int mid=a[i];
while(mid--)
{
i--;
if(i<1)break;
s[i]=true;
if(a[i]>mid)mid=a[i];
}
}
}
int ans=0;
for(int i=1;i<=n;i++){
if(s[i])
ans++;
}
cout<<ans<<endl;
return 0;
}
G | 爬山 |
题目描述
暑假学校组织同学去景区爬山,但是当地的山峰分布十分奇特,所有的山峰都分布在一条东西走向的直线上(入口在东,出口在西),为了提高同学们的身体素质学校给同学们规定了一个“运动指标”,“运动指标“等于攀爬过程中上山高度与下山的高度的和。幸运的是景区给游客提供了缆车服务(缆车的行驶路线平行于地面),游客可以在景区入口选择任意一个高度乘坐缆车但是从此以后乘坐缆车的高度不能再发生改变,由于缆车不能穿过山体,所以当缆车的行驶高度低于当前山峰高度时缆车内游客需要在当前高度的东面山体下车并步行翻过山峰,当到达西面山体的缆车高度时游客需要继续乘坐缆车直到到达景区出口。例如景区三座山峰高度分别为100米、200米、150米,当选择缆车高度为120时,第一座山峰无需攀爬,第二座山峰需要攀爬80米,第三座山峰需要攀爬30米。
位置越高缆车上的风景越好,小明希望能看到最好风景但是同时需要满足学校要求的“运动指标”。
输入描述:
第一行两个正整数N、P,N表示景区山峰数量,P表示“运动指标”。
第二行N个正整数,表示每座山峰的高度。
(1<=N<=1e5,1<=P<=1e9,1<=山峰高度<=1e9)
输出描述:
一个非负整数表示你选择的缆车高度H,如果怎样安排都无法满足则输出-1。
示例1
输入
3 230 100 200 300
输出
192
示例2
输入
3 900 150 150 125
输出
-1
解题思路
第一步先判断爬完全部的山是否可以完成任务,可以的话就对山进行排序,然后从高到低进行遍历,当只低于第一座山时每下降1的高度任务量完成加2,当低于两座山时每下降1任务完成量加4,以此类推任务完成时的高度就为最高高度
代码实现
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N];
int main(){
int n,p;
cin>>n>>p;
long long sum=0;
for(int i=1;i<=n;i++){
cin>>a[i];
sum+=a[i];
}
if(sum<p/2){
cout<<-1<<endl;
return 0;
}
sort(a+1,a+n+1);
long long mid=0;
long long kk=2;
for(int i=n;i>=1;i--){
while(a[i]>a[i-1]){
a[i]--;
mid+=kk;
if(mid>=p){
cout<<a[i]<<endl;
return 0;
}
}
kk+=2;
}
return 0;
}
I | 奶牛的寿命 |
题目描述
上帝养了一头奶牛,但是有一天奶牛偷吃了上帝草药,上帝十分生气于是奶牛必须要去地狱接受惩罚,上帝给了奶牛一个长达n的刑期但是同样允许奶牛对刑期进行最多操作log2(n)+1次,每次操作可以交换刑期的二进制形式下任意的两个位置上的数字,但是不能改变原来的二进制数的位数(不能有前导零),奶牛自然希望可以尽可能多的减刑,请问出奶牛最多可以减少多少刑期?
输入描述:
一个正整数n表示奶牛当前刑期。
(n<2^31)
输出描述:
一个整数表示奶牛最多可以减少的刑期。
示例1
输入
14
输出
3
说明
只需要交换第1位和第3位,14(1110)-->11(1011),14-11=3。
解题思路
可以去看看c++的 bitset
官方代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
//bitset返回n的31位二进制表示空位不足补前导零
bitset<31> m(n);
//如果n等于8则m为0000000000000000000000000001000
int num = m.count() - 1;//num为二进制一的个数-1
bool flag = 0;//判断有没有出现第一个1
for (int i = 30; i >= 0;i--)
{
if(i<num)m[i] = 1;
else if(flag)m[i] = 0;
if(m[i]&1) flag = 1;
}
cout <<n-m.to_ulong();
//返回一个 unsigned long 的整数值,该整数值与 bitset 具有相同的位设置
return 0;
}
自己的代码
#include<bits/stdc++.h>
using namespace std;
string s;
int main(){
long long n;
cin>>n;
int hh=n;
long long m=log(n)/log(2)+1;
int mid=0;
for(int i=1;n;i++){
s[i]=n%2+'0';
n/=2;
mid=i;
}
int kk=0;
for(int i=mid-1,j=1;i>=1;i--){
if(s[i]=='1'){
while(s[j]=='1'){
if(i==j){
kk=1;
break;
}
j++;
}
if(kk==1)break;
s[i]='0';
s[j]='1';
m--;
}
}
long long ans=0;
for(int i=mid;i>=1;i--){
ans*=2;
if(s[i]=='1')ans+=1;
}
cout<<hh-ans<<endl;
return 0;
}