[数据结构]第一章-思维题

来源:互联网 发布:襄阳seo 编辑:程序博客网 时间:2024/06/03 13:54

作业题:

1.3 相亲大会

这里写图片描述

(终于理解了最大连续子串和问题O(n)做法,开心。最朴素的做法是O(n^3),发现在第三重循环(从i到j累加)其实每次都做了重复的工作,可改进为在前一次的sum再加aj变为O(n^2)。若划分为子问题,将这一串拆成左右两个部分,中间横跨左右子串的部分(左子串的末尾一定包含,右子串的起始一定包含)相当于确定了子串末尾求往左延伸能多远,和确定了子串起始往右延伸能多远然后合并在一起,这需要O(n)时间。那么T(n) = 2*T(n/2)+O(n)。最终是个O(nlogn)的复杂度。这时我们发现,如果确定了子串的起始点或末尾点,那么就能用O(n)的时间做完。所以有了动规的做法,b[i]为以i为结束点的最大子串和,b[i] = max(b[i-1]+a[i],a[i])。发现若b[i-1]是一个负数那么显然b[i] = a[i],否则b[i] = b[i-1]+a[i]。那么就可以不用b[i]这个数组,用Max来更新遍历过程中的最大值,从第一个点开始往后加,每次更新Max,加到sum为负的时候说明现在sum包含的这一串再往后加的话已无任何贡献,舍弃掉这个点以及之前的一串,sum = 0,再从下一个点开始继续往后加。为什么这整个串都得扔掉呢,会不会有末尾的一小串的和>0呢?可以证明,如果末尾的一串>0,总和<0,那么前面的那一串和一定<0,就说明早在前面那一小串被遍历完的时候就已经扔掉了,是矛盾的。(或者理解成加到了i这个点才造成整个串<0,说明i是造成结果变小的差劲的数字,不得不舍弃掉,又因为连续子串不间断,所以前面的一串都得扔。)这就是最后的O(n)的算法。)

#include<cstdio>  #include<iostream>  #include<algorithm>   using namespace std;  int arr[100010];  int main()  {      int n;      cin >> n;      int i;      for(i = 0; i < n; i++)          cin >> arr[i];      int sum = 0;      int Max = -1000000001; //注意连续子串和可能为负,Max不可为0     for(i = 0; i < n; i++)      {          sum +=arr[i];          if(sum > Max) Max = sum;          if(sum < 0) sum = 0;      }      cout << Max << endl;      return 0;   }

同类问题:求连续子序列的和的绝对值最小值;连续子序列的长度的最小值….(待解决

1.4 心存疑惑的兰

这里写图片描述

(将这串数字sort之后从小的开始往后比较,若相邻差值大于1则较小的数字加1为答案,若不存在差值大于1,那么最大的数字加1为答案。)

#include<cstdio>  #include<iostream>  #include<algorithm>   using namespace std;  int arr[1010];  int main()  {      int n;      cin >> n;      int i;      for(i = 1; i <= n; i++)          cin >> arr[i];      sort(arr+1,arr+1+n);      bool flag = true;      for(i = 1; i <= n; i++)      {          if(arr[i-1]+1 < arr[i])  //arr[0]=0 注意没有1的情况        {              flag = false;              cout << arr[i-1]+1 << endl;              break;          }      }      if(flag) cout << arr[n]+1 << endl;      return 0;  }  

思路2:

因为n特别小只到1000,而ai的范围又特别大,那么说明这一串数字是非常离散的,只要有一个ai比1000大,那么前面就一定存在空位。所以开一个1000的bool数组flag来存数字1~1000已存在的状态,最后遍历一遍没被标记过的即为最小。

    bool flag[1002];    int n;    scanf("%d",&n);    int i,num;    for(int i = 0; i < n; i++)    {        scanf("%d",&num);        if(num < 1001 && !flag[num]) flag[num] = true;    }    i = 1;    while(flag[i]) i++;    printf("%d\n",i);

————
练习题:

1.1 单身狗进化

这里写图片描述

(先求出阶乘再求位数显然是不可取的..因为n的最大值是25000.. 考虑到一个数n的位数等于log10(n)的整数部分+1,log(n!) = log(n)+log(n-1)+…+log(1),于是可以先求出log(n!),再加一即为答案。)

#include<iostream>  #include<cstdio>  #include<cmath>  using namespace std;  int main()  {      int n;      cin >> n;      double sum = 0;      for(int i = 1; i <= n; i++)          sum+=log10((double)i);       cout << (int)sum+1 << endl;       return 0;  }  

1.2 青子的生日

这里写图片描述

(好像是挑战里的原题诶0 0 要尽可能玩的多,那么希望每个游戏都能结束得越早越好,这样就有更大机会玩更多。所以按照结束的越早越好来排序这些区间,然后从第一个开始计数,然后找到比结束时间大的下一个游戏的开始时间,cnt++,直到遍历完)

#include<iostream>  #include<cstdio>  #include<cmath>  #include<set>  #include<algorithm>  using namespace std;  int i;  struct play  {      int s;      int e;  }p[110];  bool cmp( const play & p1,const play & p2)  {      return p1.e < p2.e;  }  int main()  {      int n;      cin >> n;      for(i = 0; i < n; i++)          cin >> p[i].s >> p[i].e;      sort(p,p+n,cmp);      int cnt = 1;      int now = p[0].e;      for(i = 1; i < n; i++)      {          if(now <= p[i].s)          {              cnt++;              now = p[i].e;          }      }      cout << cnt << endl;      return 0;  }
0 0
原创粉丝点击