藍橋杯備賽(四) 枚舉,模擬與排序

藍橋杯備賽(四) 枚舉,模擬與排序

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,表示輸入數據組數。
每組數據包含兩行,第一行爲去程的起降時間,第二行爲回程的起降時間。
起降時間的格式如下:

  1. h1:m1:s1 h2:m2:s2
  2. h1:m1:s1 h3:m3:s3 (+1)
  3. 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章