Acwing4980.饥饿的牛(我的第一篇博客)

饥饿的牛 (我的第一篇博客)

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;
   
}
  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海风许愿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值