高桥和低桥
题目描述
有个脑筋急转弯是这样的:有距离很近的一高一低两座桥,两次洪水之后高桥被淹了两次,低桥却只被淹了一次,为什么?答案是:因为低桥太低了,第一次洪水退去之后水位依然在低桥之上,所以不算“淹了两次”。举例说明:
假定高桥和低桥的高度分别是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;
}