初级c语言练题之路day 1

(前面都是诡异的尝试,好写的办法在后面)

昨天机试挂了一道题,长成这样:

现在有两种面值的邮票,一种为8角,一种为6角。你要付n角的邮资(不能多付也不能少付),请给出邮票张数最少的方案。如果没有正好的方案则输出-1。

输入格式:

只有一行,为若干个整数(至少有两个)。这些整数最后一个整数一定是-1(输入结束标志,无需处理),其他整数均大于0,这些大于0的整数代表邮资。

输出格式:

若干行,每行依次对应输入的一个邮资,如果该邮资有正好的方案,则为两个用空格分隔的整数,代表张数最少的方案。前边的数字代表需要的8角的邮票的张数,后边的数字代表6角的邮票的张数;如果该邮资没有正好的方案则输出-1。测试用例保证所有整数均可以用int存储。。

大佬眼中这自然是一道水题,但是在下水平有限,考试的时候脑子一热选择了诡异的算法

如下图所示:

#include<cstdio>
#include<cstring>
using namespace std;
int main(){
    int n;
	int a=8,b=6;
	int ans1=0,ans2=0,sum=0;
	while(scanf("%d",&n)){
		if(n==-1){
			return 0;
			break;
		}
		int n0=n;
		while(n0>=2){
			n0-=2;
		}
		if(1==n0){
			printf("-1\n");
			continue;
		}
		else{
		    int biao=0;
			if(n<6){
				printf("-1\n");
				continue;
			}
			else{
				sum=0;
				ans1=0;
				ans2=0;
				while(sum<n){
				sum+=a;
				ans1+=1;
			    }
			    if(sum==n){
							biao=1;
							printf("%d %d\n",ans1,ans2);
							ans1=0;
							sum=0;
							ans2=0;
						    continue;
						
				}
			    ans1-=1;
			    sum-=a;
			    while(sum!=n){
				    if(biao==1){
						break;
					}
				    if(sum<-2){
				    	biao=1;
				    	printf("-1\n");
				    		ans1=0;
							sum=0;
							ans2=0;
				    	break;
					}
			    	while(sum<n){
			    		sum+=b;
			    		ans2++;
					}
					if(sum==n){
						if(biao==0){
							biao=1;
							printf("%d %d\n",ans1,ans2);
							ans1=0;
							sum=0;
							ans2=0;
						    continue;
						}
						
					}
					else{
					sum-=b*ans1;
					sum-=a;	
					}
					
					
			    }
				if(biao==0){
					printf("-1\n");
						ans1=0;
							sum=0;
							ans2=0;
				}
				ans1=0;
				sum=0;
				ans2=0;
			}
		}
	}
	return 0;
}

写了四十分钟,又臭又长,而且循环写到后面的时候自己都不知道在干什么了。连数据初始化的位置都忘了,最后把初始化写得到处都是。提交之后过了一组数据,但是头痛欲裂,实在不想再翻回去改这一坨不明物体。 改这东西过于折磨,不如重写,于是反手删完,从头开始。崭新代码如下

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int ans1=0,ans2=0,sum=0;
int cha(int n,int m){
	if(n<0){
		printf("-1\n");
		ans1=0;ans2=0;sum=0;
		return 0;
	}
	int n0=n;
	while(sum<n&&m==1){
		sum+=8;
		ans1++;
	}
	if(sum==n){
		printf("%d %d\n",ans1,ans2);
		ans1=0;ans2=0;sum=0;
		return 0;
	}
	else{
		sum-=8;
		ans1--;
		n0=sum;
		while(sum<n){
			sum+=6;
			ans2++;
		}
		if(sum==n){
			printf("%d %d\n",ans1,ans2);
	        ans1=0;ans2=0;sum=0;
	        return 0;
		}
		else{
			sum=n0;
			ans2=0;
			cha(n,0);
			return 0;
		}
	}
	return 0;
}
int main(){
	int n;
	while(scanf("%d",&n)){
		if(n==-1){
			break;
		}
		else{
			if(n%2==1){
				printf("-1\n");
				ans1=0;ans2=0;sum=0;
				continue;
			}
			else cha(n,1);
		}
	}
	return 0;
} 

似乎比之前好了一点,现在过了四组数据,那就稍微改一改。既然能对四组,说明整体的问题是不大的,合理怀疑在小数据的某种特殊情况出现了问题。于是我把n从1循环到100来找bug。然后在输出的数据中看见了一组非常扎眼的数据n=10 ans1=-1 ans2=3。

这玩意儿怎么还给我搞出负数来了!(メ▼へ▼)/?{︻┻┳═一

这么一想,不断的循环减数,确实容易整出负号来。于是添加了一组判断ans1 是否合法的判断。终于过了!代码如下:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int ans1=0,ans2=0,sum=0;
int cha(int n,int m){
	if(n<6){
		printf("-1\n");
		ans1=0;ans2=0;sum=0;
		return 0;
	}
	int n0=n;
	while(sum<n&&m==1){
		sum+=8;
		ans1++;
	}
	if(sum==n){
		printf("%d %d\n",ans1,ans2);
		ans1=0;ans2=0;sum=0;
		return 0;
	}
	else{
		if(sum>=1){
		sum-=8;
		ans1--;
		}
		else{
			printf("-1\n");
			return 0;
		}
		n0=sum;
		while(sum<n){
			sum+=6;
			ans2++;
		}
		if(sum==n){
			printf("%d %d\n",ans1,ans2);
	        ans1=0;ans2=0;sum=0;
	        return 0;
		}
		else{
			sum=n0;
			ans2=0;
			cha(n,0);
			return 0;
		}
	}
	return 0;
}
int main(){
	int n;
	while(scanf("%d",&n)){
		if(n==-1){
			break;
		}
		else{
			if(n%2==1){
				printf("-1\n");
				ans1=0;ans2=0;sum=0;
				continue;
			}
			else cha(n,1);
		}
	}
	return 0;
} 

还是又臭又长,而且明明刚刚学完循环和数组,按理来讲不会整出这种花活啊,怎么就整出来这一串呢?╮( ̄▽ ̄")╭

再次思考了一下,查了查题解,试了试一个单纯一点的办法,比如双重循环:

	while(scanf("%d",&n)){
		if(n==-1){
			break;
		}
		for(int i=n/8;i>=0;--i){
			for(int j=n/6;j>=0;j--){
				if(8*i+6*j==n){
					printf("%d %d\n",i,j);
					count++;
					sign=1;
				}
			}
			if(sign==1){
				sign=0;
				break;
			}
		}
		if(count==0){
			printf("-1\n");
		}
		count=0;
	}

一次就过了(○´・д・)!!!所以我之前到底在干什么。⊙ˍ⊙。为什么好好的一道题会整成这样。既然都已经走到了这一步,不如再思考一番这道题还能不能再好搞一点。于是我输出了1000以内的所有数,除开奇数之外,从12开始的所有偶数都可以被表出。于是我猜想12以后的所有偶数都能写成6,8的组合。想要证明这一点是容易的。由于24是6,8的公因数,每当贴上4张6角总是可以用3张8角代替,所以只考虑6角的张数在0~3。

不妨设2n可以被6,8表示,往证2n+2可以被6,8表示。

不妨设2n=8a+6b,2n+2=8(a-2)+6(b+3);当a<=2时的数可以被验证。由数学归纳法就知道了一个大于12的偶数确实总可以被6,8表示,而奇数总是不能被表示的。

那么,6角的张数总是0~3,这个范围是非常小的,因此对于大于12的任意的一种偶数邮资,总是可以写成8a,8a+6,8a+12,8a+18三种情况。在知道这个之后问题一下子变得简单了起来,对于任意的一个邮资,如果<=12,不是6,8就直接输出-1;>=12,奇数自然直接输出-1,偶数只要按序遍历以上4种情况,自然可以得到答案。这样,我就得到了一份简单的代码o(* ̄▽ ̄*)ブ:

#include<cstdio>
using namespace std;
int main(){
	int n,m=0;
	while(scanf("%d",&n)){if(n==-1) break;
		m=0;
		if(n<12){
			if(n==6) printf("0 1\n");
			if(n==8) printf("1 0\n");
			if(n!=6&&n!=8) printf("-1\n");
		}
		else{
			if(n%2==1){
				printf("-1\n");
				continue;
			}
			while(m<=3){
				if((n-6*m)%8==0){
					printf("%d %d\n",(n-6*m)/8,m);
					break;
				}
				m++;
			}
		}
	}
	return 0;
}

果然过了,φ(゜▽゜*)♪。但是好像也没有比双重循环好多少,不管了,好歹是自己优化出来的!而且时间复杂度要好不少,虽然还不知道怎么广泛一点,但是好耶!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值