2015编程之美挑战赛(资格赛)
一、求两个日期之间2月29日的个数
给定两个日期,计算这两个日期之间有多少个2月29日(包括起始日期)。
只有闰年有2月29日,满足以下一个条件的年份为闰年:
1. 年份能被4整除但不能被100整除
2. 年份能被400整除输入
第一行为一个整数T,表示数据组数。
之后每组数据包含两行。每一行格式为”month day, year”,表示一个日期。month为{“January”, “February”, “March”, “April”, “May”, “June”, “July”, “August”, “September”, “October”, “November” , “December”}中的一个字符串。day与year为两个数字。
数据保证给定的日期合法且第一个日期早于或等于第二个日期。输出
对于每组数据输出一行,形如”Case #X: Y”。X为数据组数,从1开始,Y为答案。
1.用了一个结构体数组来存年月日,其实也可以用二维整形数组,不过这样好理解一些;
2.在主计算函数里费了些时间,想着把两个端点及他们之间的年分开来算,但是一开始不知道怎样算之间有多少个闰年,后来用了遍历的方法可能会耗一点时。然后就是没有考虑两个年数相同以及同为闰年的情况。
3.main函数里主要有输入模块和对每个case 调用函数输出模块,将字符串月份转换为int本来想用switch,但是switch的参数必须为一个数,所以不行。好像枚举类型可行,但是最后采用了if-else这种丑陋的结构。
#include<iostream>
#include<vector>
#include<string>
using namespace std;
typedef struct mdy
{
mdy(int m,int d,int y):month(m),day(d),year(y){}
int month;
int day;
int year;
}mdy;
//判断是否为闰年
bool isLeap(int year)
{
if(year%400==0||(year%4==0&&year%100!=0))return true;
else return false;
}
//主计算函数
int computeLeap(int beginYear,int beginMonth,int beginDay,int endYear,int endMonth,int endDay)
{
int leapNum=0;
if(endYear==beginYear&&isLeap(beginYear))
{
if(((beginMonth<2)||(beginMonth==2&&beginDay<=29))&&((endMonth>2)||(endMonth==2&&endDay==29)))
leapNum++;
}
for(int i=beginYear+1;i<endYear;i++)
{
if(isLeap(i))leapNum++;
}
if(endYear!=beginYear&&isLeap(beginYear))
{
if(beginMonth<2)leapNum++;
else if(beginMonth==2&&beginDay<=29)leapNum++;
}
if(endYear!=beginYear&&isLeap(endYear))
{
if(endMonth>2)leapNum++;
else if(endMonth==2&&endDay==29)leapNum++;
}
return leapNum;
}
int main()
{
vector<mdy> vecMdy;
int T;
cin>>T;
T*=2;
//输入
for(int i=0;i<T;i++)
{
string sMonth;
int month,day,year;
cin>>sMonth>>day;
getchar();
cin>>year;
if(sMonth=="January")month=1;
else if(sMonth=="February")month=2;
else if(sMonth=="March")month=3;
else if(sMonth=="April")month=4;
else if(sMonth=="May" )month=5;
else if(sMonth=="June" )month=6;
else if(sMonth=="July")month=7;
else if(sMonth=="August" )month=8;
else if(sMonth=="September" )month=9;
else if(sMonth=="October" )month=10;
else if(sMonth=="November")month=11;
else if(sMonth=="December")month=12;
struct mdy mymdy(month,day,year);
vecMdy.push_back(mymdy);
}
int k=1;
vector<mdy>::iterator pos;
for(pos=vecMdy.begin();pos!=vecMdy.end();++pos)
{
int leapnum=computeLeap(pos->year,pos->month,pos->day,(pos+1)->year,(pos+1)->month,(pos+1)->day);
cout<<"Case #"<<k<<":"<<" "<<leapnum<<endl;
k++;
++pos;
}
return 0;
}
二、回文子串数目
给定字符串,求它的回文子序列个数。回文子序列反转字符顺序后仍然与原序列相同。例如字符串aba中,回文子序列为”a”, “a”, “aa”, “b”, “aba”,共5个。内容相同位置不同的子序列算不同的子序列。
输入
第一行一个整数T,表示数据组数。之后是T组数据,每组数据为一行字符串。输出
对于每组数据输出一行,格式为”Case #X: Y”,X代表数据编号(从1开始),Y为答案。答案对100007取模。
回文字符串相关的问题很容易在笔试或面试中出出来,这道题我当时一开始是不会的,所以也不打算做了,不过我觉得我应该弄懂它。
以下来自cbsheng的专栏
所谓回文字符串,就是一个字符串,从左到右读和从右到左读是完全一样的。
解法:递归
递归的作用在于把问题的规模不断缩少,直到问题缩少到能简单地解决
问:如何缩少问题规模?
答:通过观察可以知道,一个回文字符串其中内部也是回文。所以,我们只需要以去掉两端的字符的形式一层层检查,每一次的检查都去掉了两个字符,这样就达到了缩少问题规模的目的。新问题与原问题有着相同的形式
当去掉两端字符后的字符串,其产生的新问题同样是检查这个字符串是否回文。递归的结束需要简单情景
1. 字符串长度可能会奇数或偶数:
如果字符串长度是奇数,字符串会剩下最中间那位字符,但其不影响回文。当检查到长度为1的时候即代表此字符串是回文
如果字符串长度是偶数,当两端的字符串两两比较检查后不会剩下字符。即检查到长度为0的时候即代表此字符串是回文
2. 如果检查到两端两个字符不相同。则说明此字符串不是回文,直接返回0,不需要继续检查递归跳跃的信任
此题的递归分解比较简单,所以对在递归过程中细节的实现,我们可以直接看出。但是一些较复杂的题目上,我们就没那么容易看出过程中细节的实现,这时候就需要我们递归跳跃的信任!
#include <iostream>
using namespace std;
int fun(int low, int high, char *str, int length)
{
if (length == 0 || length == 1)
return 1;
if (str[low] != str[high])
return 0;
return fun(low+1, high-1, str, length-2);
}
int main()
{
char str[]="aaabdaaa";
int length = strlen(str);
//返回1代表是, 0代表不是
cout << fun(0, length-1, str, length) << endl;
return 0;
}