2017年蓝桥杯C++组真题解析2
题目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;
*/