2017年蓝桥杯C++组真题解析2

2019-03-23  本文已影响0人  你要好好学习呀
题目7:日期问题

小明正在整理一批历史文献。这些历史文献中出现了很多日期。小明知道这些日期都在1960年1月1日至2059年12月31日。
令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。
更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。
比如02/03/04,可能是2002年03月04日、2004年02月03日或2004年03月02日。
给出一个文献上的日期,你能帮助小明判断有哪些可能的日期对其对应吗?
输入:
一个日期,格式是"AA/BB/CC"。 (0 <= A, B, C <= 9)
输出:
输出若干个不相同的日期,每个日期一行,格式是"yyyy-MM-dd"。多个日期按从早到晚排列。
样例输入
02/03/04
样例输出
2002-03-04
2004-02-03
2004-03-02

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms

#include <bits/stdc++.h>
using namespace std;
bool isLeap(int year){
    return ((year%4==0 && year%100!=0)|| year&400==0);
}
void i2s(int n,string &str){//注意
    stringstream ss;
    ss<<n;
    ss>>str;
}
string f(int a,int b,int c){
    if(a>=0 && a<=59)a+=2000;
    else if(a>=60 && a<=99)a+=1900;
    else return "";

    if(b<1 || b>12) return "";
    if(c<1 || c>31) return "";

    bool _isLeap=isLeap(a);
    switch(b){//日期校验
        case 2:
            if(_isLeap && c>29) return "";
            if(!_isLeap && c>28) return "";
            break;
        case 4:
            if(c>30) return "";
            break;
        case 6:
            if(c>30) return "";
            break;
        case 9:
            if(c>30) return "";
            break;
        case 11:
            if(c>30) return "";
            break;
        default:
            break;

    }
    string _a,_b,_c;
    i2s(a,_a);
    i2s(b,_b);
    i2s(c,_c);
    if(_b.length()==1)_b="0"+_b;////C++中不能使用string型的="0"+int型的--重要
    if(_c.length()==1)_c="0"+_c;
    return _a+"-"+_b+"-"+_c;
}

int main()
{
    string in;
    cin>>in;
    int a=0,b=0,c=0;
    a=(in[0]-'0')*10+(in[1]-'0');//单个字符转数字
    b=(in[3]-'0')*10+(in[4]-'0');
    c=(in[6]-'0')*10+(in[7]-'0');
    string case1=f(a,b,c);
    string case2=f(c,a,b);
    string case3=f(c,b,a);
    //注意排序和去重
    set<string> ans;//set中的元素都是排好序的,set中没有重复的元素
    if(case1!="") ans.insert(case1);
    if(case2!="") ans.insert(case2);
    if(case3!="") ans.insert(case3);
    //迭代器的使用来遍历set中的元素
    /*
    begin(),返回set容器第一个元素的迭代器
    end(),返回一个指向当前set末尾元素的下一位置的迭代器.
    */
    for(set<string>::iterator iter=ans.begin();iter!=ans.end();iter++){
        cout<<*iter<<endl;
    }//重要
    return 0;
}
题目8:包子凑数

小明几乎每天早晨都会在一家包子铺吃早餐。
他发现这家包子铺有N种蒸笼,其中第i种蒸笼恰好能放Ai个包子。每种蒸笼都有非常多笼,可以认为是无限笼。
每当有顾客想买X个包子,卖包子的大叔就会迅速选出若干笼包子来,使得这若干笼中恰好一共有X个包子。比如一共有3种蒸笼,分别能放3、4和5个包子。当顾客想买11个包子时,大叔就会选2笼3个的再加1笼5个的(也可能选出1笼3个的再加2笼4个的)。

当然有时包子大叔无论如何也凑不出顾客想买的数量。
比如一共有3种蒸笼,分别能放4、5和6个包子。而顾客想买7个包子时,大叔就凑不出来了。

小明想知道一共有多少种数目是包子大叔凑不出来的。
输入

第一行包含一个整数N。(1 <= N <= 100)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100)
输出

一个整数代表答案。如果凑不出的数目有无限多个,输出INF。

例如:
输入:
2
4
5

程序应该输出:
6

再例如,
输入:
2
4
6

程序应该输出:
INF

样例解释:
对于样例1,凑不出的数目包括:1, 2, 3, 6, 7, 11。
对于样例2,所有奇数都凑不出来,所以有无限多个。

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms

#include <bits/stdc++.h>
using namespace std;

int n;//n种蒸笼
int g;//最大公约数
bool f[10000];
int a[101];
//最大公约数,是指公有的因数中最大的那个
//辗转相除法:辗转相除法是求两个自然数的最大公约数的一种方法,也叫 欧几里德算法
//最小公倍数=X/gcd(x,y)*Y
int gcd(int a,int b){//重要
    if(b==0) return a;//递归出口
    return gcd(b,a%b);
}
int main(){
    scanf("%d",&n);
    f[0]=true;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        if(i==1) g=a[i];//初始化最大公约数
        else g=gcd(a[i],g);
        for(int j=0;j<10000;j++){
            if(f[j])f[j+a[i]]=true;
        }
    }
    if(g!=1){
        printf("INF\n");
        return 0;//不要忘记
    }
    //统计个数
    int ans=0;
    for(int k=0;k<10000;k++)
    {
        if(!f[k]){
            ans++;
            //cout<<k<<endl;
        }
    }
    printf("%d",ans);
}
题目9:分巧克力

儿童节那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。
小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形。
为了公平起见,小明需要从这 N 块巧克力中切出K块巧克力分给小朋友们。切出的巧克力需要满足:
1. 形状是正方形,边长是整数
2. 大小相同
例如一块6x5的巧克力可以切出6块2x2的巧克力或者2块3x3的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小Hi计算出最大的边长是多少么?

输入
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含两个整数Hi和Wi。(1 <= Hi, Wi <= 100000)
输入保证每位小朋友至少能获得一块1x1的巧克力。

输出
输出切出的正方形巧克力最大可能的边长。

样例输入:
2 10
6 5
5 6

样例输出:
2

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms

//先暴力枚举,再考虑如何优化

#include <bits/stdc++.h>
using namespace std;
int n,k;
int h[100000];
int w[100000];
/*贪心暴力枚举
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
    {
        cin>>h[i]>>w[i];
    }
    for(int len=100000;len>0;len--)//枚举切成的巧克力块的边长
    {
        int cnt=0;
        for(int j=0;j<n;j++){//枚举大巧克力块数
            cnt+=(h[j]/len)*(w[j]/len);
        }
        if(cnt>=k){
            cout<<len<<endl;
            return 0;
        }
    }
    return 0;
}
*/
//使用二分法试探-满足则试探更大的,更新结果;否则试探更小的
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
    {
        cin>>h[i]>>w[i];
    }
    int r=100001;
    int l=1;
    int ans=0;
    while(l<=r)
    {
        int mid=(l+r)/2;//试探边长
        int cnt=0;
        for(int j=0;j<n;j++){//枚举大巧克力块数
            cnt+=(h[j]/mid)*(w[j]/mid);
        }
        if(cnt>=k){
            l=mid+1;
            ans=mid;
        }else{
            r=mid-1;
        }
    }
    cout<<ans<<endl;
    return 0;
}
题目10:K倍区间

给定一个长度为N的数列,A1, A2, ... AN,如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,
我们就称这个区间[i, j]是K倍区间。
你能求出数列中总共有多少个K倍区间吗?

输入
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)

输出
输出一个整数,代表K倍区间的数目。

例如,
输入:
5 2
1
2
3
4
5

程序应该输出:
6

//暴力枚举复杂度太高,可以使用前缀和来优化-但还不是太高效
/*
#include <bits/stdc++.h>
using namespace std;
int n,k;
int a[100010];
int s[100010];

int main()
{
    cin>>n>>k;
    s[0]=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        s[i]=s[i-1]+a[i];
    }
    long long ans=0;
    //枚举i和j
    for(int i=1;i<=n;i++){
        for(int j=i;i<=n;j++)
        {
            if((s[j]-s[i-1])%k==0)
                ans++;
        }
    }
    return 0;
}*/
//用同余的知来解
// 若amodm==bmodm,则 a 和 b 之差是 m 的倍数)
#include <bits/stdc++.h>
using namespace std;
int n,k;
int a[100010];
int s[100010];
map<int,int> cnt;//同余个数统计

int main()
{
    cin>>n>>k;
    s[0]=0;
    cnt[0]=1;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        s[i]=(s[i-1]+a[i])%k;
        cnt[s[i]]++;
    }
    long long ans=0;
    for(int i=0;i<k;i++){//余数必然在0-k-1之间
        ans+=(long long)cnt[i]*(cnt[i]-1)/2;//例如所有前缀和中%k==3的有3个,则它们中任选2个可组合出一种结果(3*2)/2
    }
    cout<<ans<<endl;
    return 0;
}
/*
 Map是c++的一个标准容器,她提供了很好一对一的关系,在一些程序中建立一个map可以起到事半功倍的效果,总结了一些map基本简单实用的操作!
1. map最基本的构造函数;
   map<string , int >mapstring;         map<int ,string >mapint;
   map<sring, char>mapstring;         map< char ,string>mapchar;
   map<char ,int>mapchar;            map<int ,char >mapint;
2. map添加数据;
   map<int ,string> maplive;
   1.maplive.insert(pair<int,string>(102,"aclive"));
   2.maplive.insert(map<int,string>::value_type(321,"hai"));
   3, maplive[112]="April";//map中最简单最常用的插入添加!
3,map中元素的查找:
   find()函数返回一个迭代器指向键值为key的元素,如果没找到就返回指向map尾部的迭代器。
   map<int ,string >::iterator l_it;
   l_it=maplive.find(112);
   if(l_it==maplive.end())
                cout<<"we do not find 112"<<endl;
   else cout<<"wo find 112"<<endl;
4,map中元素的删除:
   如果删除112;
   map<int ,string >::iterator l_it;;
   l_it=maplive.find(112);
   if(l_it==maplive.end())
        cout<<"we do not find 112"<<endl;
   else  maplive.erase(l_it);  //delete 112;
*/
上一篇下一篇

猜你喜欢

热点阅读