饥饿的牛 (我的第一篇博客)
2023.07.09
原题链接Acwing4980.饥饿的牛
1 暴力思路
编程始于暴力 优化以暴力为基础
本题思路直接从第一天暴力枚举到第T天,统计其中有草的天数
但是因为本题数据过大 已经超出10的9次方 成功收获 Time Limit Exceeded
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long LL; //因为很多数据都超出了int的范围,所以要使用long long
const int N=100010;
int n;//n个送草计划
LL T; //总天数
LL d[N];//记录计划di 哪一天
LL b[N];//记录计划bi 食物
LL sum;//记录此时有多少捆草
LL t;//作为d数组的下标索引
LL res;//记录有草的一天
int main(){
cin>>n>>T;
int i1=0; //作为while的循环下标
int n1=n;//保护一下n
while(n1--){
cin>>d[i1]>>b[i1]; //在第di天送了bi捆草
i1++;
}
for(LL i=0;i<T;i++) {
if(i==d[t]-1)sum+=b[t++]; //在di天给了草
if(sum>0){
sum--;
res++;
}
}
cout<<res;
return 0;
}
2 自己优化后的代码
这是我自己根据上面暴力的代码进行的优化
思路是直接通过计算相邻两次投递失物马儿吃草的情况
若是这次投递前已经没有草了,说明马儿挨饿了 统计天数时,只统计有草的天数
若是这次投递时刚刚好没草或者有草,那么说明草一直有,直接统计相邻两次投递草的数量
*整体是在第i天投草时,判断上一次投草和这次投草之间这几天的吃草情况 左闭右开 不考虑今天*
不过代码可能有一些问题,最后只通过了大部分样例,有一个数据一直是wrong answer
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long LL; //因为很多数据都超出了int 的范围,所以要使用long long
const int N=100010;
LL n;//n个送草计划
LL T; //总天数
LL d[N];//记录计划di 哪一天
LL b[N];//记录计划bi 食物
LL sum;//记录此时还有多少捆草 也就是剩余的草的数目
LL t=1;//作为d数组的下标索引
LL res;//记录有草的一天
int main(){
cin>>n>>T;
LL i1=1; //作为while的循环下标
LL n1=n;//保护一下n
while(n1--){
cin>>d[i1]>>b[i1]; //在第di天送了bi捆草
// cout<<d[i1]<<' '<<b[i1]<<endl;
i1++;
}
// cout<<d[1]<<endl;
while(1) {
//直接从有草的那天开始算起
if(n>1){ //如果n不等于1 说明投了多次草
t++; //作为下标索引
if(d[t]-d[t-1]>b[t-1]+sum){ /*若是这次投递前已经没有草了,说明马儿挨饿了
统计天数时,只统计有草的天数*/
sum=0;
res+=b[t-1];
}
else {
sum=b[t-1]+sum-(d[t]-d[t-1]);/*若是这次投递时刚刚好没草或者有草,那么说明草一直有,
直接统计相邻两次投递草的数量*/
res+=d[t]-d[t-1];
}
if(t==n) //边界单独处理一下
{
if(d[t]==T)res++; //若是最后一天投递 直接res++ 因为只剩这一天可以吃草了
else if(d[t]<T){ //其他情况 要判断投递完的草够不够吃的吃到最后一天
if(sum+b[t]>T-d[t])res+=T-d[t]+1; //若是够,直接统计据最后一天还剩几天
else res=res+sum+b[t]; //若是不够 统计草的数量
}
break;
}
}
if(n==1){
if(b[1]>T)res=T;
else res=b[1];
break;} //若n=1 说明只投食物一次 只计算这次的投递情况即可
}
cout<<res;
return 0;
}
3 看完y总视频后对自己代码的纠正
这次优化了代码量 采用了max和min的思想
省去了大量的if判断语句
总结以上错因 主要是有的代码的逻辑前后顺序搞反了
就比如这题
sum=max(0LL,b[i-1]+sum-d[i]+d[i-1]); //若是不够吃的,说明没剩草
res+=min(d[i]-d[i-1],b[i-1]+sum) ; //统计相隔的天数多还是草多
// !!!!!!错误写法!!!!!!!!!
我把sum的这条语句放在了res的前面 这样的话 sum提前更新了,导致res里面再次用到sum时,sum已经是更新过了的语句
这种后面的用到了前面变量的代码,一定要注意先后顺序,看看它是不是提前更新
这次狠狠的给我长了教训了
4 完整答案
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long LL; //因为很多数据都超出了int 的范围,所以要使用long long
const int N=100010;
LL n;//n个送草计划
LL T; //总天数
LL d[N];//记录计划di 哪一天
LL b[N];//记录计划bi 食物
LL sum;//记录此时还有多少捆草 也就是剩余的草的数目
LL res;//记录有草的天数
int main(){
cin>>n>>T;
LL i1=1; //作为while的循环下标
LL n1=n;//保护一下n
while(n1--){
cin>>d[i1]>>b[i1]; //在第di天送了bi捆草
i1++;
}
d[n+1]=T+1; //最后一天这一天也要考虑在内,所以多加一天
if(n==1){ //若是只投递一次草,单独考虑
if(b[1]>T)res=T;
else res=b[1];
} //若n=1 说明只投食物一次 直接进行判断即可
else{
for(int i=1;i<=n+1;i++){ //若n>1 则说明投递多次草
//只算有草的节点 通过相邻两次
res+=min(d[i]-d[i-1],b[i-1]+sum) ; //统计相隔的天数多还是草多
sum=max(0LL,b[i-1]+sum-d[i]+d[i-1]); //若是不够吃的,说明没剩草
//这两行顺序是不能颠倒的 因为res里面用到了sum 如果sum在上,那么sum会被提前更新
}
}
cout<<res;
return 0;
}