满n送一问题,顾名思义就是满了n个就送你一个。举个小学数学例子,班长买饮料,三个空瓶换一瓶,最少买几瓶?我先展示一个算法:
#include<stdio.h>
#define NUM 4 //满NUM送1
int fun1(int n){ //通过递归在买n瓶的情况下我们可以得到多少瓶
if(n<NUM)
return n;
else
return n+fun1(n/NUM);
}
int fun2(int a){ //需求a瓶我们要买多少瓶
int u;
for(int i=0;i<=a;i++){
if(fun1(i)>=a){
printf("%d\n",i);
break;
}
}
}
int main(){
for(int b=1;b<=200;b++){ //所需求的瓶数b不断增加
printf("%d:",b);
fun2(b);
}
}
是不是很简单?
但是!这个算法是错的。它忽略了一点。就比如说满三送一的话,假设我们买5瓶,可以得到多少瓶?5瓶中的3瓶可以换1瓶,但是余数2和换来的1又可以换一瓶,总共7瓶。但是这个算法忽略了余数2,所以结果为6,错误。也就是说它忽略了上一次计算的余数。
那么这个程序呢?
#include<stdio.h>
#define NUM 4 //满NUM送1
int fun1(int n){ //通过递归在买n瓶的情况下我们可以得到多少瓶
if(n<NUM)
return n;
else
return n+fun1((n+n%NUM)/NUM);
}
int fun2(int a){ //需求a瓶我们要买多少瓶
int u;
for(int i=0;i<=a;i++){
if(fun1(i)>=a){
printf("%d\n",i);
break;
}
}
}
int main(){
for(int b=1;b<=200;b++){ //所需求的瓶数b不断增加
printf("%d:",b);
fun2(b);
}
}
这个算法也是不对的,因为它加的不是上一次的余数而是本次的余数。那么要如何改正呢?由于要加上一次的余数,这时候递归就不太好用了,所以我们选择循环。
#include<stdio.h>
#define NUM 4 //满NUM送1
int fun1(int n){ //在买n瓶的情况下我们可以得到多少瓶
int a=0;
int sum=n;
int b=0;
while(n>0){
a=(n+b)/NUM;
sum=sum+a;
b=(n+b)%NUM;
n=n/3;
}
return sum;
}
int fun2(int a){ //需求a瓶我们要买多少瓶
int u;
for(int i=0;i<=a;i++){
if(fun1(i)>=a){
printf("%d\n",i);
break;
}
}
}
int main(){
for(int b=1;b<=200;b++){ //所需求的瓶数b不断增加
printf("%d:",b);
fun2(b);
}
}
这样结果就对了。然后我们发现如果满n送一,我们要买a瓶,那么结果就在a*((n-1)/n)左右。算是偷懒快速解题的思路吧。