藍橋杯備賽(四) 枚舉,模擬與排序
1.Acwing 1210. 連號區間數
小明這些天一直在思考這樣一個奇怪而有趣的問題:
在 1∼N的某個排列中有多少個連號區間呢?
這裏所說的連號區間的定義是:
如果區間 [L,R]裏的所有元素(即此排列的第 L 個到第 R個元素)遞增排序後能得到一個長度爲 R−L+1 的“連續”數列,則稱這個區間連號區間。
當 NN很小的時候,小明可以很快地算出答案,但是當 N 變大的時候,問題就不是那麼簡單了,現在小明需要你的幫助。
輸入格式
第一行是一個正整數 N,表示排列的規模。
第二行是 N個不同的數字 Pi,表示這 N個數字的某一排列。
輸出格式
輸出一個整數,表示不同連號區間的數目。
數據範圍
1≤N≤10000
1≤Pi≤N
輸入樣例1:
4
3 2 4 1
輸出樣例1:
7
輸入樣例2:
5
3 4 2 5 1
輸出樣例2:
9
//題目中排列暗示數組無重複
//將數列依次+1遞增轉化爲滿足最大值-最小值等於下標之差
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=10001,INF=1000000;
int a[N],n;
int main()
{
int count=0;
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n;i++) //枚舉左端點
{
int Max=-INF,Min=INF;
for(int j=i;j<n;j++) //枚舉右端點
{
Max=max(Max,a[j]);
Min=min(Min,a[j]);
if(Max-Min==j-i) //滿足最大值-最小值等於下標之差的,符合要求
count++;
}
}
cout<<count;
getchar();getchar();
return 0;
}
2.Acwing 1236. 遞增三元組
https://www.acwing.com/problem/content/1238/
法1:前綴和
//前綴和做法
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
const int N=100010;
int a[N],b[N],c[N],n;
int cnt[N]; //cnt[i]表示i出現的次數
int s[N]; //前綴和數組,s[i]表示1 ~ i總共出現的次數
int as[N],cs[N]; //分別表示小於b[i],大於b[i]的個數
int main()
{
cin>>n;
for(int i=0;i<n;i++) scanf("%d",&a[i]);
for(int i=0;i<n;i++) scanf("%d",&b[i]);
for(int i=0;i<n;i++) scanf("%d",&c[i]);
for(int i=0;i<n;i++) cnt[a[i]]++;
for(int i=0;i<N;i++) s[i]=s[i-1]+cnt[i];
for(int i=0;i<n;i++) as[i]=s[b[i]-1]; //小於b[i]的個數
memset(cnt,0,sizeof(cnt)); //用過了cnt,別忘了清0
for(int i=0;i<n;i++) cnt[c[i]]++;
for(int i=0;i<N;i++) s[i]=s[i-1]+cnt[i];
for(int i=0;i<n;i++) cs[i]=s[N-1]-s[b[i]]; //大於b[i]的個數
LL res=0; //由於數據範圍可能會爆int,所以用long long
for(int i=0;i<n;i++) res=res+(LL)as[i]*cs[i];
getchar();getchar();
return 0;
}
法二:sort+二分
二分STL用法:https://blog.csdn.net/qq_40160605/article/details/80150252
//sort+二分查找做法(使用STL實現二分)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long lld;
const int N = 100005;
int a[N], b[N], c[N];
int n;
lld sum;
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
scanf("%d", &b[i]);
for (int i = 1; i <= n; i++)
scanf("%d", &c[i]);
//由於二分的前提是單調序列 所以預先對a b c排序 直接sort
sort(a + 1, a + 1 + n);
sort(b + 1, b + 1 + n);
sort(c + 1, c + 1 + n);
for (int i = 1; i <= n; i++) //對b數組中一個一個b[i]去二分查找比b[i]大的與比b[i]小的
{
//直接用STL中的兩個二分函數解決
lld x = (lower_bound(a + 1, a + 1 + n, b[i]) - a) - 1; //在數組a中找比b[i]小的數
lld y = n - (upper_bound(c + 1, c + 1 + n, b[i]) - c) + 1; //在數組c中找比b[i]大的數
sum += x * y;
}
printf("%lld", sum);
getchar();getchar();
return 0;
}
3.Acwing 1245
小明對數位中含有 2、0、1、9的數字很感興趣(不包括前導 00),在 1到 40 中這樣的數包括 1、2、9、10 至 32、39 和 40,共 28 個,他們的和是 574。
請問,在 11 到 nn 中,所有這樣的數的和是多少?
輸入格式
共一行,包含一個整數 n。
輸出格式
共一行,包含一個整數,表示滿足條件的數的和。
數據範圍
1≤n≤10000
輸入樣例:
40
輸出樣例:
574
#include<iostream>
#include<cstdio>
using namespace std;
int n,s,t;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
int x=i;
while(x!=0)
{
t=x%10;
x=x/10; //把每一位取出來判斷
if(t==2||t==1||t==0||t==9)
{
s=s+i;
break;
}
}
}
cout<<s;
getchar();getchar();
return 0;
}
4.Acwing 1204
某涉密單位下發了某種票據,並要在年終全部收回。
每張票據有唯一的ID號。
全年所有票據的ID號是連續的,但ID的開始數碼是隨機選定的。
因爲工作人員疏忽,在錄入ID號的時候發生了一處錯誤,造成了某個ID斷號,另外一個ID重號。
你的任務是通過編程,找出斷號的ID和重號的ID。
假設斷號不可能發生在最大和最小號。
輸入格式
第一行包含整數 N,表示後面共有 N 行數據。
接下來 N 行,每行包含空格分開的若干個(不大於100個)正整數(不大於100000),每個整數代表一個ID號。
輸出格式
要求程序輸出1行,含兩個整數 m,n用空格分隔。
其中,m表示斷號ID,n表示重號ID。
數據範圍
1≤N≤100
輸入樣例:
2
5 6 8 11 9
10 12 9
輸出樣例:
7 9
#include<iostream>
#include<cstdio>
#include<cstring>
#include<sstream>
#include<algorithm>
using namespace std;
const int N=10010;
int n,cnt;
int a[N];
int main()
{
cin>>n;
string line; //getline用於讀取一行數據,可以讀空格,遇到換行符或EOF結束
getline(cin,line); //讀取掉第一行的回車
while(n--)
{
getline(cin,line); //讀取結果放在line中
stringstream ssin(line);
while(ssin>>a[cnt]) cnt++; //從line中讀取,每次讀取放在a[]中
}
sort(a,a+cnt);
int res1=0,res2=0;
for(int i=1;i<cnt;i++)
{
if(a[i]==a[i-1]) res2=a[i];
else if(a[i]>=a[i-1]+2) res1=a[i]-1;
}
cout<<res1<<" "<<res2<<endl;
getchar();getchar();
return 0;
}
5.Acwing 466 迴文日期
在日常生活中,通過年、月、日這三個要素可以表示出一個唯一確定的日期。
牛牛習慣用 8 位數字表示一個日期,其中,前 4 位代表年份,接下來 2 位代表月份,最後 2 位代表日期。
顯然:一個日期只有一種表示方法,而兩個不同的日期的表示方法不會相同。
牛牛認爲,一個日期是迴文的,當且僅當表示這個日期的8位數字是迴文的。
現在,牛牛想知道:在他指定的兩個日期之間(包含這兩個日期本身),有多少個真實存在的日期是迴文的。
一個 8 位數字是迴文的,當且僅當對於所有的 i(1≤i≤8) 從左向右數的第i個數字和第 9−i 個數字(即從右向左數的第 i 個數字)是相同的。
例如:
•對於2016年11月19日,用 8 位數字 20161119 表示,它不是迴文的。
•對於2010年1月2日,用 8 位數字 20100102 表示,它是迴文的。
•對於2010年10月2日,用 8 位數字 20101002 表示,它不是迴文的。
輸入格式
輸入包括兩行,每行包括一個8位數字。
第一行表示牛牛指定的起始日期date1,第二行表示牛牛指定的終止日期date2。保證date1和date2都是真實存在的日期,且年份部分一定爲4位數字,且首位數字不爲0。
保證date1一定不晚於date2。
輸出格式
輸出共一行,包含一個整數,表示在date1和date2之間,有多少個日期是迴文的。
輸入樣例:
20110101
20111231
輸出樣例:
1
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a,b,ans,res;
int d[13] = {0,31,29,31,30,31,30,31,31,30,31,30,31};//每個月的天數
int main()
{
scanf("%d %d",&a,&b);
for(int i=1;i<=12;i++)
{
for(int j=1;j<=d[i];j++)
{
//一位位的處理,最終枚舉出迴文數
ans=j%10*10000000+j/10*1000000+i%10*100000+i/10*10000+i/10*1000+i%10*100+j/10*10+j%10;
if(ans<=b&&ans>=a)
res++;
}
}
cout<<res;
getchar();getchar();
return 0;
}
6.Acwing 1219 移動距離
X星球居民小區的樓房全是一樣的,並且按矩陣樣式排列。
其樓房的編號爲 1,2,3…1,2,3…
當排滿一行時,從下一行相鄰的樓往反方向排號。
比如:當小區排號寬度爲 6 時,開始情形如下:
1 2 3 4 5 6
12 11 10 9 8 7
13 14 15 …
我們的問題是:已知了兩個樓號 mm 和 nn,需要求出它們之間的最短移動距離(不能斜線方向移動)。
輸入格式
輸入共一行,包含三個整數 w,m,n,w 爲排號寬度,m,n爲待計算的樓號。
輸出格式
輸出一個整數,表示 m,n兩樓間最短移動距離。
數據範圍
1≤w,m,n≤10000
輸入樣例:
6 8 2
輸出樣例:
4
//本題爲一個數值蛇形排列的矩陣,求兩點的曼哈頓距離
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int w,m,n;
int main()
{
cin>>w>>m>>n;
m--;n--; //所有數減一便於操作
int x1,x2,y1,y2;
x1=m/w;x2=n/w;
y1=m%w;y2=n%w;
//偶數行反轉
if(x1%2) y1=w-1-y1;
if(x2%2) y2=w-1-y2;
cout<<abs(x1-x2)+abs(y1-y2);
getchar();getchar();
return 0;
}
7.Acwing 1229 日期問題
小明正在整理一批歷史文獻。這些歷史文獻中出現了很多日期。
小明知道這些日期都在1960年1月1日至2059年12月31日。
令小明頭疼的是,這些日期採用的格式非常不統一,有采用年/月/日的,有采用月/日/年的,還有采用日/月/年的。
更加麻煩的是,年份也都省略了前兩位,使得文獻上的一個日期,存在很多可能的日期與其對應。
比如02/03/04,可能是2002年03月04日、2004年02月03日或2004年03月02日。
給出一個文獻上的日期,你能幫助小明判斷有哪些可能的日期對其對應嗎?
輸入格式
一個日期,格式是”AA/BB/CC”。
即每個’/’隔開的部分由兩個 0-9 之間的數字(不一定相同)組成。
輸出格式
輸出若干個不相同的日期,每個日期一行,格式是”yyyy-MM-dd”。
多個日期按從早到晚排列。
數據範圍
0≤A,B,C≤9
輸入樣例:
02/03/04
輸出樣例:
2002-03-04
2004-02-03
2004-03-02
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
//日期問題套路
int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
bool check(int year, int month, int day) //判斷日期是否符合規定
{
if (month == 0 || month > 12) return false;
if (day == 0) return false;
//對2月特殊處理
if (month != 2)
{
if (day > days[month]) return false;
}
else
{
int leap = year % 100 && year % 4 == 0 || year % 400 == 0; //若爲閏年則leap=1
if (day > 28 + leap) return false;
}
return true;
}
int main()
{
int a, b, c;
scanf("%d/%d/%d", &a, &b, &c);
for (int date = 19600101; date <= 20591231; date ++ ) //直接在所給範圍內枚舉所有情況
{
int year = date / 10000;
int month = date % 10000 / 100;
int day = date % 100;
if (check(year, month, day)) //判斷當前枚舉是否符合年月日的規則
{
if (year % 100 == a && month == b && day == c || // 年/月/日
month == a && day == b && year % 100 == c || // 月/日/年
day == a && month == b &&year % 100 == c) // 日/月/年
printf("%d-%02d-%02d\n", year, month, day); //%02d可以實現自動補零效果
}
}
getchar();getchar();
return 0;
}
8.getline用法
getline()的原型:
頭文件:#include
istream& getline ( istream &is , string &str , char delim );
其中,istream &is 表示一個輸入流,譬如cin;
string&str表示把從輸入流讀入的字符串存放在這個字符串中(可以自己隨便命名,str什麼的都可以);
char delim表示遇到這個字符停止讀入,在不設置的情況下系統默認該字符爲’\n’,也就是回車換行符(遇到回車停止讀入)。
例子:
int main()
{
string line:
while(getline(cin,line))
cout<<line<<endl;
return 0;
}
C++ string的back與front
string a=“abcd”;
1.獲取字符串最後一個字符
auto b=a.back(); //結果爲 b=‘d’;
2.修改字符串最後一個字符
a.back()=’!’; //結果爲 a=“abc!”;
3.獲取字符串第一個字符
auto b=a.front(); //結果爲 b=‘a’;
4.修改字符串第一個字符
a.front()=’!’; //結果爲 a="!bcd";
acwing 1231 航班時間
小 h 前往美國參加了藍橋杯國際賽。
小 h 的女朋友發現小 h 上午十點出發,上午十二點到達美國,於是感嘆到“現在飛機飛得真快,兩小時就能到美國了”。
小 h 對超音速飛行感到十分恐懼。
仔細觀察後發現飛機的起降時間都是當地時間。
由於北京和美國東部有 12 小時時差,故飛機總共需要 14 小時的飛行時間。
不久後小 h 的女朋友去中東交換。
小 h 並不知道中東與北京的時差。
但是小 h 得到了女朋友來回航班的起降時間。
小 h 想知道女朋友的航班飛行時間是多少。
對於一個可能跨時區的航班,給定來回程的起降時間。
假設飛機來回飛行時間相同,求飛機的飛行時間。
輸入格式
一個輸入包含多組數據。
輸入第一行爲一個正整數 T,表示輸入數據組數。
每組數據包含兩行,第一行爲去程的起降時間,第二行爲回程的起降時間。
起降時間的格式如下:
- h1:m1:s1 h2:m2:s2
- h1:m1:s1 h3:m3:s3 (+1)
- h1:m1:s1 h4:m4:s4 (+2)
第一種格式表示該航班在當地時間h1時m1分s1秒起飛,在當地時間當日h2時m2分s2秒降落。
第二種格式表示該航班在當地時間h1時m1分s1秒起飛,在當地時間次日h2時m2分s2秒降落。
第三種格式表示該航班在當地時間h1時m1分s1秒起飛,在當地時間第三日h2時m2分s2秒降落。
輸出格式
對於每一組數據輸出一行一個時間hh:mm:ss,表示飛行時間爲h小時m分s秒。
注意,當時間爲一位數時,要補齊前導零,如三小時四分五秒應寫爲03:04:05。
數據範圍
保證輸入時間合法(0≤h≤23,0≤m,s≤59),飛行時間不超過24小時。
輸入樣例:
3
17:48:19 21:57:24
11:05:18 15:14:23
17:21:07 00:31:46 (+1)
23:02:41 16:13:20 (+1)
10:19:19 20:41:24
22:19:04 16:41:09 (+1)
輸出樣例:
04:09:05
12:10:39
14:22:05
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int get_second(int h,int m,int s)
{
return h*3600+m*60+s;
}
int get_time()
{
string line;
getline(cin,line);
if(line.back()!=')') line+="(+0)"; //統一格式
int h1,m1,s1,h2,m2,s2,d;
sscanf(line.c_str(),"%d:%d:%d %d:%d:%d (+%d)",&h1,&m1,&s1,&h2,&m2,&s2,&d); //sscanf()會將參數str的字符串根據參數format字符串來轉換並格式化數據
//c_str()函數返回一個指向正規c字符串的指針,內容和string類的本身對象是一樣的,通過string類的c_str()函數能夠把string對象轉換成c中的字符串的樣式;
return get_second(h2,m2,s2)-get_second(h1,m1,s1)+d*24*3600;
}
int main()
{
int n;
cin>>n;
string line;
getline(cin,line); //讀掉第一行的回車
while(n--)
{
int time=(get_time()+get_time())/2; //轉化爲秒計算
int hour=time/3600,minute=time%3600/60,second=time%60;
printf("%02d:%02d:%02d\n",hour,minute,second);
}
getchar();getchar();
return 0;
}
9.Acwing 1241. 外賣店優先級
“飽了麼”外賣系統中維護着 N 家外賣店,編號 1∼N。
每家外賣店都有一個優先級,初始時 (0 時刻) 優先級都爲 0。
每經過 1個時間單位,如果外賣店沒有訂單,則優先級會減少 1,最低減到 0;而如果外賣店有訂單,則優先級不減反加,每有一單優先級加 2。
如果某家外賣店某時刻優先級大於 5,則會被系統加入優先緩存中;如果優先級小於等於 33,則會被清除出優先緩存。
給定 T時刻以內的 M 條訂單信息,請你計算 T 時刻時有多少外賣店在優先緩存中。
輸入格式
第一行包含 3 個整數 N,M,TN,M,T。
以下 M 行每行包含兩個整數 ts 和 id,表示 ts 時刻編號 id 的外賣店收到一個訂單。
輸出格式
輸出一個整數代表答案。
數據範圍
1≤N,M,T≤105
1≤ts≤T
1≤id≤N
輸入樣例:
2 6 6
1 1
5 2
3 1
6 2
2 1
6 2
輸出樣例:
1
樣例解釋
6 時刻時,1 號店優先級降到 3,被移除出優先緩存;2 號店優先級升到 6,加入優先緩存。
所以是有 1 家店 (2 號) 在優先緩存中。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=100010;
int n,m,T,cnt; //last[i]表示第i個店鋪上一次有訂單的時刻
int score[N],last[N]; //score[i]表示第u個店鋪當前的優先級
bool st[N]; //st[i]表示第i個店鋪當前是否處於優先緩存之中
pair<int,int> order[N]; //pair可以進行雙關鍵字排序,若第一個相同,則根據第二個排序
int main()
{
scanf("%d%d%d",&n,&m,&T);
for(int i=0;i<m;i++) scanf("%d%d",&order[i].first,&order[i].second);
sort(order,order+m);
for(int i=0;i<m;) //對同一店同一時刻的訂單一起處理,即一批批處理
{
//分批
int j=i;
while(j<m&&order[j]==order[i]) j++;
int t=order[i].first,id=order[i].second;
cnt=j-i; //cnt計數
i=j; //更新i
//
int tspan=t-last[id]-1;
score[id]=score[id]-tspan; //沒有訂單的時候score減小
if(score[id]<0) score[id]=0;
if(score[id]<=3) st[id]=false;
score[id]+=2*cnt;
if(score[id]>5) st[id]=true;
last[id]=t; //更新last[id]
}
//對每一個餐館的最後一次訂單一直到結束的這段時間,score需要減小
for (int i=1;i<=n;i++)
{
if (last[i] < T)
{
score[i]-=T-last[i];
if (score[i] <= 3) st[i] = 0;
}
}
int ans = 0;
for (int i = 1; i <= n; i++) ans += st[i];
cout << ans << endl;
getchar();getchar();
return 0;
}