洛谷 NOIP2016 普及组 回文日期 + 洛谷 NOIP2017 图书管理员

回文日期这题目本来是不难想思路的。。。。。。

然而我第一次做的时候改了蛮久才把代码完全改对,主要感觉还是不够细心,敲的时候也没注意见检查一些小错误,那么接下来不说废话,请看题干:

洛谷 NOIP2016 普及组 回文日期 + 洛谷 NOIP2017 图书管理员_第1张图片

接下来请看输入输出的样例以及数据范围:

洛谷 NOIP2016 普及组 回文日期 + 洛谷 NOIP2017 图书管理员_第2张图片

我提供两个方法去做这个题,第一个思路是:

首先我们分析容易看出输入的日期其实当作两个八位数处理即可,然后直接循环,从起始日期到截止日期,如果是回文日期,直接ans++即可,只是我们需要思考如何来让日期进行正确的进位,所以这时候我考虑使用函数来达到目的的功能,接下来请看代码:

#include
using namespace std;
int d1,d2,ans;
int getday(int y,int m){
	//y年m月有多少天
	if(m==1 || m==3 || m==5 || m==7 || m==8 || m==10 || m==12)
	return 31;
	if(m==4 || m==6 || m==9 || m==11)
    return 30;
    //判断闰年
    if(( !(y%4) && y%100) || !(y%400))
    return 29;
    else 
	return 28;
}
int nextday(int x){
	//x的下一天  表示为八位整数
	int y=x/10000;
	int m=x/100%100;
	int d=x%100;
	if(d != getday(y,m))
	++d;
	else if(m!=12){
		++m;
		d=1;
	}
	else {
		y+=1;
		m=1;
		d=1;
	}
	return 10000*y+100*m+d;
}
int rev(int x){
	int ans=0;
	while(x){
		ans=ans*10+x%10;
		x/=10;
	}
	return ans;
}
int main(){
	scanf("%d%d",&d1,&d2);
	for(int d=d1;d<=d2;d=nextday(d)){
		if(d == rev(d)) ans++;
	}
    printf("%d\n",ans);
	return 0;
}

 看起来很长其实也并不复杂,写了几个函数,第一个函数是判断对应的月份有多少天,第二个是判断循环中的下一天的日期,第三个循环判断翻转数字是否与一开始的一样,也就是判断回文数,然后就是很简单的输入和枚举,相信不必进行解释了。

接下来我就在思考,很明显一开始的方法还是进行了很多不必要的循环,两个回文日期的间隔一般是比较大的,所以有很多不必要的循环,也就是我考虑如何降低循环的次数,同时也能降低时间复杂度,接下来请看代码:

#include
using namespace std;
int d1,d2,ans;
int getday(int y,int m){
	//y年m月有多少天
	if(m==1 || m==3 || m==5 || m==7 || m==8 || m==10 || m==12)
	return 31;
	if(m==4 || m==6 || m==9 || m==11)
    return 30;
    //判断闰年
    if(( !(y%4) && y%100) || !(y%400))
    return 29;
    else 
	return 28;
}
int nextday(int x){
	//x的下一天  表示为八位整数
	int y=x/10000;
	int m=x/100%100;
	int d=x%100;
	if(d != getday(y,m))
	++d;
	else if(m!=12){
		++m;
		d=1;
	}
	else {
		y+=1;
		m=1;
		d=1;
	}
	return 10000*y+100*m+d;
}
int rev(int x){
	int ans=0;
	while(x){
		ans=ans*10+x%10;
		x/=10;
	}
	return ans;
}
int main(){
	scanf("%d%d",&d1,&d2);
    for(int y=1000;y<=9999;y++){
    	int revy=rev(y);
    	int m=revy/100;
    	int d=revy%100;
    	if(m>=1 && m<=12 && d>=1 && d<=getday(y,m)){
    		int x=y*10000+revy;
    		if(x>=d1 && x<=d2) ans++;
		}
	}
	printf("%d\n",ans);
	return 0;
}

这个方法就是将年份进行了循环,从1000循环到9999年,每次循环先判断出是否是回文年份并且这个回文年份是否在输入的起始日期和截止日期之间,如果在,那么就可以ans++,这是因为我们可以发现日期是一个八位数字,并且由于年份、月份和日子都不可以为0,所以我们其实可以把年份看成特殊进位的数字,进而只需要循环一万次不到就可以得到结果。

看第二道题:图书管理员:

洛谷 NOIP2016 普及组 回文日期 + 洛谷 NOIP2017 图书管理员_第3张图片

接下来看输入输出以及测试案例以及数据范围:
洛谷 NOIP2016 普及组 回文日期 + 洛谷 NOIP2017 图书管理员_第4张图片 这道题目因为数据范围比较小,所以没什么难度,直接暴力枚举就可以了,直接两重循环来枚举,看每个借书的是否能从图书馆里面的书找到自己需要的相匹配即可,这个可以通过函数来进行实现,不过由于当多本书满足的时候优先输出编号小的,所以可以先对书的编号按照从小到大的顺序进行排序,话不多说代码如下:

#include
#include
using namespace std;
int n,q;
int book[1010],a[1010],l[1010];
bool pd(int x,int y,int z){
	int b[11];
	int t=1;
	while(y--){
		b[t]=x%10;
		x/=10;
		if(z%10!=b[t]){
			return false;
		}
		z/=10;
		++t;  
	}
	return true;
}
int main(){
	cin>>n>>q;
	for(int i=1;i<=n;i++)
	cin>>book[i];
	sort(book+1,book+n+1);
	for(int i=1;i<=q;i++)
	cin>>l[i]>>a[i];
	for(int i=1;i<=q;i++){
		int k=-1;
	    for(int j=1;j<=n;j++){
	    	if(pd(book[j],l[i],a[i])){
	    	cout<

我和一个同学都做了这个题,接下来我展示一下他的代码:

#include
using namespace std;
int n,q;
int a[1100];
int main(){
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	for(int i=1;i<=q;i++){
		int l,b;
		scanf("%d%d",&l,&b);
		int pw=1;
		for(int j=0;j

不得不承认他的代码更加的简洁,思路大致就是利用a数组来存每一本图书的编号,然后由于有q个人去借书,同样是枚举然后进行判断是否能借到自己想要的书本,引用了ans作为中间变量,未使用排序,从第一本书枚举到了最后一本书,当遇见能够匹配的书时,如果ans的值还没有改变也就是为-1,那么就将ans赋值为对应编号,如果在后面找到了更小的编号得到匹配就更新ans,最后如果没有找到能匹配的书籍由于ans的初始值为-1只需要直接输出ans即可。

两种方法其实都是暴力枚举,读者可以根据自己的习惯来进行挑选。

你可能感兴趣的:(算法,c++,贪心)