高桥和低桥

题目描述

有个脑筋急转弯是这样的:有距离很近的一高一低两座桥,两次洪水之后高桥被淹了两次,低桥却只被淹了一次,为什么?答案是:因为低桥太低了,第一次洪水退去之后水位依然在低桥之上,所以不算“淹了两次”。举例说明:
假定高桥和低桥的高度分别是5和2,初始水位为1
第一次洪水:水位提高到6(两个桥都被淹),退到2(高桥不再被淹,但低桥仍然被淹)
第二次洪水:水位提高到8(高桥又被淹了),退到3。
没错,文字游戏。关键在于“又”的含义。如果某次洪水退去之后一座桥仍然被淹,那么下次洪水来临水位提高时不能算“又”淹一次。
输入n座桥的高度以及第i次洪水的涨水水位ai和退水水位bi,统计有多少座桥至少被淹了k次。初始水位为1,且每次洪水的涨水水位一定大于上次洪水的退水水位。

输入

输入文件最多包含25组测试数据。每组数据第一行为三个整数n, m, k(1<=n,m,k<=10^5 第二行为n个整数hi(2<=hi<=10 ^ 8),即各个桥的高度。以下m行每行包含两个整数ai和bi(1<=bi<ai<=10 ^8, ai>bi-1)。输入文件不超过5MB。

输出

对于每组数据,输出至少被淹k次的桥的个数。

样例输入

2 2 2
2 5
6 2
8 3
5 3 2
2 3 4 5 6
5 3
4 2
5 2

样例输出

Case 1: 1
Case 2: 3

二分练习
数据太大,暴力会超时。涨潮退潮无规律,可以对桥的高低排序,找到符合要求的两个边界(1、上一次退潮后没被淹,这一次被淹的最低的桥 2、这一次能够被淹的最高的桥),然后对边界内的桥被淹次数进行计数,最后统计。
可以自己写二分查找,也可以利用lower_bound( )和upper_bound( ),来寻找范围。

一、

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int h[maxn],f[maxn],v[maxn];


//二分寻找最低的符合要求的桥
int ferfen(int start,int end,int water,int l){
    int mid=(start+end)/2;
    if(start==end||start+1==end){//二分结束
    	//上界符合要求,返回更小的上界
        if(h[start]>water&&h[start]<=l) return start;
        return end; 
    }
    //如果mid符合要求,就要往小继续二分,寻找更小的符合要求的值。
    if((h[mid]>water&&h[mid]<=l)||h[mid]>l) return ferfen(start,mid,water,l);
    else return ferfen(mid,end,water,l);
}

//二分寻找最高的符合要求的桥
int lerfen(int start,int end,int water,int l){
    int mid=(start+end)/2;
    if(start==end||start+1==end){
    	//下界符合要求,返回更大的下界
        if(h[end]>water&&h[end]<=l) return end;
        return start; 
    }
    //如果mid符合要求,就要往大继续二分,寻找更大的符合要求的值。
    if((h[mid]>water&&h[mid]<=l)||h[mid]<=water) return lerfen(mid,end,water,l);
    else return lerfen(start,mid,water,l);
}


int main(){
    int n,m,k,c=0;
    while(~scanf("%d%d%d",&n,&m,&k)){
        c++;
        int a,b,ans=0,water=1;
        memset(f,0,sizeof(f));
        memset(v,0,sizeof(v));
        for(int i=1;i<=n;i++){
            scanf("%d",&h[i]);
        }
        sort(h+1,h+n+1);
        for(int i=0;i<m;i++){
            scanf("%d%d",&a,&b);
            int s=ferfen(1,n,water,a);
            int e=lerfen(1,n,water,a);
            water=b;//记录上一次退潮之后水的位置
            for(int i=s;i<=e;i++) v[i]++;
        }
        for(int i=1;i<=n;i++){
            if(v[i]>=k) ans++;
        }
        printf("Case %d: %d\n",c,ans);
    }
    return 0;
}

lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int h[maxn],v[maxn];

int main(){
    int n,m,k,c=0;
    while(~scanf("%d%d%d",&n,&m,&k)){
        c++;
        int a,b,ans=0,water=1;
        memset(v,0,sizeof(v));
        for(int i=0;i<n;i++){
            scanf("%d",&h[i]);
        }
        sort(h,h+n);
        for(int i=0;i<m;i++){
            scanf("%d%d",&a,&b);
            //返回第一个大于等于 water+1 的值得地址,-h,获得对应下标
            int s=lower_bound(h,h+n,water+1)-h;
            //返回第一个大于 a 的值的地址,-h-1,获得最后一个小于或者等于的值的下标
            int e=upper_bound(h,h+n,a)-h-1;
            water=b;
            for(int i=s;i<=e;i++) v[i]++;
        }
        for(int i=0;i<n;i++){
            if(v[i]>=k) ans++;
        }
        printf("Case %d: %d\n",c,ans);
    }
    return 0;
}

汉诺塔III

题目描述

“汉诺塔”是一个众所周知的古老游戏。
现在我们把问题稍微改变一下:如果一共有4根柱子,而不是3根,那么至少需要移动盘子多少次,才能把所有的盘子从第1根柱子移动到第4根柱子上呢?
为了编程方便,您只需要输出这个结果mod 10000的值。

输入

该题含有多组测试数据,每组一个正整数n。(0<n<=50000)

输出

一个正整数,表示把n个盘子从第1根柱子移动到第4根柱子需要的最少移动次数mod 10000的值。

样例输入

15

样例输出

129

四柱汉诺塔问题,A、B、C、D四根柱子,要把A上的n个圆盘移动到D柱上,规则和普通汉诺塔一样。
首先,把圆盘分为两部分,下方k个,上方n-k个。n-k个利用C、D柱移动到B柱,剩下k个利用C柱移动到D柱(这样就是普通三柱汉诺塔问题)。
列出所有k的可能,找到移动次数最少的解。
m[i]存放移动次数,那总移动次数为m[n]=2*m[n-k]+2^k-1

for(int i=1;i<=100;i++){
      m[i]=1000000000;
      ll temp;
      for(int j=1;j<=i;j++){
      		//计算移动次数
          temp=(2*m[i-j])%10000+((ll)pow(2,j)-1)%10000;
          if(temp<m[i]){
              m[i]=temp;
              //记录移多少圆盘最优
              k[i]=j;
          }
   	}
}

But,0<n<=50000,数字太大,直接遍历会超时,所以找规律

1 1
2 3=1+2
3 5=3+2
4 9=5+4
5 13=9+4
6 17=13+4
7 25=17+8
8 33=25+8
9 41=33+8
10 49=41+8
11 65=49+16
12 81=65+16
13 97=81+16
14 113=97+16
15 129=113+16

发现2加了两次,4加了三次,8加了四次,然后打表,输出就OK了。
代码写的不好,见谅!

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+5;
#define maxx 1000000000
ll m[maxn]={0,1};
  
int main(){
    int n,ans1=2,ans2=2;
    for(int i=2;i<=50000;i++){
        int j=ans2;
        while(j--){
            if(i>50000) break;
            m[i]+=m[i-1]+ans1;
            m[i]%=10000;
            i++;
        }
        i--;
        ans1*=2;
        ans1%=10000;
        ans2++;
    }
    while(~scanf("%d",&n)){
         
        printf("%lld\n",m[n]);
    }
    return 0;
}