第三周学习总结

第三周学习总结

一、
本篇目主要是Vjudge上的习题总结
题目网址:https://vjudge.net/contest/426531
A.
1.要把握好int的范围,否则可能导致数值过大,造成越界
2.要做到最优原则,应先排序后通过二重循环,防止超时

for(i=0;i<n;i++)
    for(j=i+1;j<n;j++)
    sum+=(a[i]-a[j]);

结果使sum*2即可,可以提高一倍的效率
3.数组的定义必须是常量
B.
1.思路:应该按照天数从大向前排列,合理使用优先队列,事半功倍

bool cmp1(pri a,pri b){if(a.b!=b.b) return a.b>b.b;}
sort(p,p+n,cmp1);
priority_queue<int> q;
maxb=p[0].b;
     for(j=maxb;j>=1;j--)
     {
         while(p[i].b==j)
       {
        q.push(p[i].a);
        i++;
       }
       if(q.empty()==0)
       {sum+=q.top();
        q.pop();}
  }

2.输入时使用while() {…},可以实现多次输出
C.
1.思路:按照横坐标从小到大排列
思路与给出n个数字区间,使一个数字可以涵盖几个区间,求数字最小的个数,使可以涵盖所有区间题相似

bool cmp(point a,point b)
{if(a.x!=b.x) return a.x<b.x;}

求出第一个点作为半径上的点时,圆心横坐标的最大值,从第二个点开始循环查找,如果下一个点的圆心横坐标的最大值小于前一个,则圆心的点改变(此时圆内包含前一个点和后一个点)
判断条件:

tmpx=p[i].x+sqrt((double)(r*r-p[i].y*p[i].y));
         if(tmpx<=x)
         {x=tmpx;}

继续向后扫描,遇到下一个点的圆心横坐标的最大值大于前一个,且不在前一个圆心所在的圆内的时候,雷达数量+1,同时新的圆心产生(新的圆心即该点圆心横坐标的最大值)
判断条件:

if((p[i].x-x)*(p[i].x-x)+p[i].y*p[i].y>r*r)
            {sum++;
             x=tmpx;}

2.没有读完题,导致不知道 “-1”安装意味着在这种情况下没有解决方案
D.
其实一开始我是没看清楚题,一直在思考重叠的坑要怎么办,例如输入时出现(2,9)和(5,10)或者(2,9)和(5,7),而事实上这种情况并不存在,浪费了大量的时间
仔细审题后思路其实挺清楚,按照起点位置进行从小到大的排序,然后进行铺木板,首先判断铺一个木板可以盖住几个坑,即判断木板长度和下一个坑结束长度的关系,再判断铺的木板的长度和下一个坑开始距离的关系,取临时木板铺到的长度为temp,用max函数比较temp和下一个坑的开始距离,取大值,最后用while语句进行木板挨个铺设,输出数量,这样的好处,既可以省去判断末位置和木板距离是否可以被L整除,又可以避免繁琐的if语句,大大减少了代码的复杂度,又能使程序的运算速度更快
代码奉上

#include <iostream>
#include <algorithm>
using namespace std;
struct pool
{int a,b;}p[10010];
bool cmp(pool a,pool b){return a.a<b.a;}
int main()
{
ios::sync_with_stdio(false);
    int n,L,i,temp=0,sum=0;
    cin>>n>>L;
    for(i=0;i<n;i++)
        cin>>p[i].a>>p[i].b;
    sort(p,p+n,cmp);
    for(i=0;i<n;i++)
     {
      if(temp>p[i].b) continue;
      temp=max(temp,p[i].a);
      while(temp<p[i].b)
       {sum++;temp+=L;}
     }
    cout<<sum;
} 

看完代码不难发现,代码打破了惯性思维,即前后顺序的思维,正常思维写出的代码应该是先判断铺一块木板之后的距离,然后再用坑末位置减temp,再用末位置减temp除L,算出s加到sum上,如果这样的话,难免会有非常复杂的循环和if语句的判断,而以上代码中包含了另一种思维,先在循环外赋值,然后再运用再赋值,需要先用初值为0的temp进行判断,还需要在temp为初值的情况下与坑的起始值进行max判断,最后一个while语句结束循环!简洁至极!其实我认为如果想写出如此简洁的代码,思路清晰是最重要的,之后便是对定义的变量temp的清楚认知,清晰它所储存的值所代表的意义
F.
思路:该题的解题思路挺简单,只要按照每个仓库每磅可以换得爪哇豆的数量按照降序对仓库进行排序

bool cmp(food a,food b)
{return a.average>b.average;}
f[i].average=f[i].j/f[i].F;

最后在m不足以换取整个仓库的时候,用该仓库的average乘以剩余的m,最后输出sum
题目需要使用头文件

#include <iomanip>

在输出时使用setprecision()函数进行输出位数的限制,使用fixed使setprecision()函数从小数点后开始计算
J.
思路:思路其实跟B题的思路非常相似,先按照作业的截止时间进行降序排列,从最后一天开始循环,每一重循环过后天数-1,在每次天数-1之前,把所有的减分的值排到优先数列q中,然后再用所有科目的减分之和进行相减,使用优先数列对天数的最大值priority_queueq;进行从大到小的排列,可以减少sort代码的使用,在对优先数列q进行操作时,需要进行判断q是否为空,否则该天不对sum和q进行操作

if(q.empty()!=1)
     {sum-=q.top();
     q.pop();}

H.
整体思路就是,先按照长度和质量升序排列,然后从第一个开始往后挨个筛选,如果符合要求,标记,否则跳过,遇到标记的直接跳过,先按照木材的长度进行从小到大的排序,在木材长度相等的情况下按照木材的重量进行升序排列

bool cmp(wood a,wood b)
{
    if(a.l==b.l)
        return a.w<b.w;
        return a.l<b.l;
}

然后并且需要定义一个整型数组,且该数组初始化为0,该数组作用为记录每一个位置是否被使用(即是否已经符合长度和重量都大于或等于前面的木材),如果是,则赋值该数组此位置为1,如果不是则跳过,在之后的判断语句中如果该位置为1,则即可跳过该位置的判断,一个结束一次大循环sum+1

for(k=0; k<n; k++)
        {

            if(q[k]) continue;
            temp=p[k].w;
            for(j=k+1; j<n; j++)
            {
                if(q[j]==0&&temp<=p[j].w) 
                {
                    q[j]=1;
                    temp=p[j].w;
                }
            }
            sum++;
        }

其实仔细一想,只要删除一行语句,即可实现我一开始错误题意,即temp=p[j].w,这个思想其实在题打牌游戏中也出现过,定义一个为0数组,如果牌已经出了就把该位置赋值为1,在if语句判断时可以减少复杂度,本题有异曲同工之妙,让使用过的数字赋值为1
K.
嘿嘿嘿.题目一反惯例,竟然给了中文,直接跳过了翻译阶段
思路:首先最主要的是排序,先按照结束时间进行排序,在结束时间一样的情况下则按照开始时间进行排序,都是从小到大进行排序,排序工具cmp定义如下

bool cmp(tv a,tv b)
{
    if(a.end!=b.end)
        return a.end<b.end;
}

这样想其实不难解释,因为看完上一个节目看下一个节目时,需要参照上一个节目的结束时间进行下一个节目的选择,如果上一个节目的结束时间越早,下一个节目选择的空间就越大,可选择的节目就越多,接下来只用一个for循环就能解决问题(sum初值为1)

for(i=1;i<n;i++)
        {
            if(temp>p[i].begin)
                continue;
            else
            {
                temp=p[i].end;
                sum++;
            }
        }

其实一开始的思路并没有想到按照结束时间进行升序排列,而是用开始时间进行排列,然后再使用复杂的if语句对结束时间进行判断,不过代码的复杂程度太大,便马上换了思路
N&&I.
思路1:使用优先数列,先用数组和优先数列结合,输入给数列my赋值,利用循环给数列yours赋值,然后利用循环,使用top()函数和pop()函数进行比较

for(;my.empty()==0;)
         {
           if(my.top()>yours.top())
              {sum++;
               my.pop();}
            else {my.pop();yours.pop();}

思路2:先把自己的牌输入,然后再定义一个较大的数组,可以放下所有牌,并用memset函数进行赋0操作(0即false),在较大的数组内符合自己的牌的数字,并赋值为1(即ture),然后找到除了自己的牌最大的牌的数字,在循环内进行比较,如果自己的牌大于其他玩家最大的牌,num+1,否则其他玩家最大的牌使用,并定义为ture,再在剩余数组内找到较大数组内为0的最大数

int a=n*m,num=0;
        while(sum[a]==1)
            a--;
        for(i=0;i<n;i++)
        {
            if(my[i]>a)
                num++;
            else
            {
                sum[a]=1;
                while(sum[a]==1)
                    a--;
            }

T.
首先要说的是这个题的题意真的是太难理解了,看了一个小时的题目,硬是没看懂是什么意思…
思路:求出一个集合,该集合包含每个区间的至少两个元素,求集合的最小元素的个数,首先进行输入,并且按照末尾坐标进行升序排列,定义temp_a和temp_b 是相邻的两个点的坐标,使其

   temp_a=p[0].end-1;
    temp_b=p[0].end;

sum初始值为2,然后进行if语句判断,如果下一个区间的最小值大于temp_b,sum+2,更新temp_a,temp_b(该情况为下一个区间和上一个区间没有任何交集),如果下个区间的最大值大于temp_b且temp_a不在下一个区间,sum+1且temp_a=temp_b,temp_b等于下一个区间的最大值(该情况为下一个区间只包含temp_b,需要再加一个元素使下一区间包含两个)

if(temp_b<p[i].begin)
        {
            sum+=2;
            temp_a=p[i].end-1;
            temp_b=p[i].end;
        }
        else if(temp_a<p[i].begin&&temp_b>=p[i].begin)
        {
            sum++;
            temp_a=temp_b;
            temp_b=p[i].end;
        }

X.
多说无益,直接上代码吧

#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
    int n,i,sum=0,p[101];
    cin>>n;
    for(i=0;i<n;i++)
        cin>>p[i];
    sort(p,p+n);
    for(i=0;i<n/2+1;i++)
    sum+=p[i]/2+1;
    cout<<sum;
} 

二、
感悟总结
1.经过这一个星期的做题让我感触最深的就是读题审题和做题技巧,如果读题审题不好,轻则修改一下思路,修改一下代码,重则重新思考思路,重新写代码,其浪费的时间可想而知,为什么不多用点时间去读题审题而是去花更多的时间去做不符合题目要求的题呢?不仅浪费时间,更消耗心态
2.还有一个就是做题技巧,这似乎让我又找到了高中的感觉,不能刚读完题就开始int a,b,i;…要先有一个解题思路,想一下大概需要用到几个变量,会用到什么函数,最后思考一下这样的思路有没有更简单的代码可以实现,自问自己没有做到这一点,从而让我浪费了更多的时间去修改思路,修改代码。
3.这一周通过做题其实了解到了很多做题技巧比如扑克牌和木材的题,都运用了一种思想,定义整形数组,并赋值为0,如果该位置已经使用,则赋值为1,在if判断语句中如果遇到1,则可以直接跳过。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值