某个人的常量可能是其他人的变量
文章目录
💒1.喝汽水问题
🌟1.1题目描述
🌻1.2 思路
方法1:
- 20元首先可以喝20瓶,此时手中有20个空瓶子
- 两个空瓶子可以喝一瓶,喝完之后,空瓶子剩余:empty/2(两个空瓶子换的喝完后产生的瓶子) + empty%2(不够换的瓶子)
- 如果瓶子个数超过1个,可以继续换,即重复2
方法2:
按照上述喝水和用瓶子换的规则的话,可以发现,其实就是个等差数列:money*2-1
其实也可以换一个思路证明:1瓶汽水买1元,2个空瓶换1瓶汽水,那20元就相当于40个空瓶,但无论怎么置换最后都会剩下1个空瓶(可以向老板借1个空瓶就不会剩下了且可以多喝1瓶🌝),所以就是39瓶即2n-1.
🍅1.3 代码演示
方法1:
int main()
{
int money = 0;
int total = 0;
int empty = 0;
scanf("%d", &money);
total = money;
empty = money;
while(empty>1)
{
total += empty/2;
empty = empty/2+empty%2;
}
return 0;
}
方法2:
int main()
{
int money = 0;
int total = 0;
int empty = 0;
scanf("%d", &money);
if(money <= 0)
{
total = 0;
}
else
{
total = money*2-1;
}
printf("total = %d\n", total);
return 0;
}
💒2.调整奇数偶数顺序
🌟2.1题目描述
🌻2.2思路
- 给定两个下标left和right,left放在数组的起始位置,right放在数组中最后一个元素的位置
- 循环进行一下操作
a. 如果left和right表示的区间[left, right]有效,进行b,否则结束循环
b. left从前往后找,找到一个偶数后停止
c. right从后往前找,找到一个奇数后停止
d. 如果left和right都找到了对应的数据,则交换,继续a,
🍅2.3代码演示
void swap_arr(int arr[], int sz)
{
int left = 0;
int right = sz-1;
int tmp = 0;
while(left<right)
{
// 从前往后,找到一个偶数,找到后停止
while((left<right)&&(arr[left]%2==1))
{
left++;
}
// 从后往前找,找一个奇数,找到后停止
while((left<right)&& (arr[right]%2==0))
{
right--;
}
// 如果偶数和奇数都找到,交换这两个数据的位置
// 然后继续找,直到两个指针相遇
if(left<right)
{
tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
}
}
💒3. 杨辉三角
🌟3.1题目描述
🌻3.2 思路
由于此题要打印整个杨辉三角的数据而非取出某一项,所以不可避免的一定是要填出每一项,没有偷懒的余地,那就老老实实的根据规律填空即可。按照题设的场景,能发现数字规律为:d[i][j] = d[i - 1][j] + d[i - 1][j - 1]。所以我们只要按照这个方法填表即可。
初始化:
操作:
改进:
由于我在填第n行的杨辉三角时,只跟第n-1行的杨辉三角产生联系,不会跟之前的有联系,所以没必要保存每一行的杨辉三角,填一行打一行就行了,这样能让空间复杂度从n^2降低到n。但是在填数据的时候不能对之前的数据覆盖,所以需要从后向前填。而填杨辉三角顺序对结果是没有影响的,所以可以实现。
这种方法虽然降低了空间复杂度,但只能保存最后一行的数据,不利于反复查询,两个填法各有各的适用场景。就本题而言,改进后的胜出。
初始化:
操作:
🍅3.3代码演示
void yangHuiTriangle(int n)
{
int data[30][30] = { 1 }; //第一行直接填好,播下种子
int i, j;
for (i = 1; i < n; i++) //从第二行开始填
{
data[i][0] = 1; //每行的第一列都没有区别,直接给1,保证不会越界。
for (j = 1; j <= i; j++) //从第二列开始填
{
data[i][j] = data[i - 1][j] + data[i - 1][j - 1]; //递推方程
}
}
for (i = 0; i < n; i++) //填完打印
{
for (j = 0; j <= i; j++)
{
printf("%d ", data[i][j]);
}
putchar('\n');
}
}
改进:
void yangHuiTriangle(int n)
{
int data[30] = { 1 };
int i, j;
printf("1\n"); //第一行就直接打印了
for (i = 1; i < n; i++) //从第二行开始
{
for (j = i; j > 0; j--) //从后向前填,避免上一行的数据在使用前就被覆盖
{
data[j] += data[j - 1]; //公式同上,由于变成了一维,公式也变简单了。
}
for (j = 0; j <= i; j++) //这一行填完就直接打印了。
{
printf("%d ", data[j]);
}
putchar('\n');
}
}
💒4.猜凶手
🌟4.1题目描述
🌻4.2思路
分别假设凶手是a,b,c,d,看谁是凶手时满足3个人说了真话,一个人说了假话
🍅4.3 代码演示
#include<stdio.h>
int main()
{
int killer = 0;
for (killer = 'a'; killer <= 'd'; killer++)
{
if ((killer != 'a') + (killer == 'c') + (killer == 'd') + (killer != 'd') == 3)
printf("凶手是:%c", killer);
}
return 0;
}
💒5 猜名次
🌟5.1 题目描述
🌻5.2 思路
考虑到一共五个人,直接模拟推理有些太难,计算机最擅长的遍历此时就会派上用场,将每个人从第1到第5来一遍,则一共会产生5^5种可能性,这个只需要一个5层循环即可搞定。但是这样会导致一些不期望出现的结果出现,因为我并没有查重,所以会出现两个人抢名次的情况,也就是两个人或者更多的人名次相同的情况,例如两个第二,三个第三这样的,所以即使满足了条件,也要查看一下五个人的名次是否重复,这个交给一个函数来执行,只要五个人名次并列,那就返回0,否则返回1即可。有了这个思路,就能完成以下代码。
🍅5.3代码演示:
#include <stdio.h>
int checkData(int *p)
{
int tmp[7] = { 0 }; //标记表,实际是哈希表的思路。一开始每个元素都是0。
int i;
for (i = 0; i < 5; i++)
{
if (tmp[p[i]]) //如果这个位置的标记已经是1,则代表重复,直接返回0。
{
return 0;
}
tmp[p[i]] = 1; //如果不是,则给这个位置标记为1。
}
return 1; //全部标记完毕也没有出现重复的情况,代表OK。
}
int main()
{
int p[5]; //0 1 2 3 4分别代表a b c d e
for (p[0] = 1; p[0] <= 5; p[0]++)
{
for (p[1] = 1; p[1] <= 5; p[1]++)
{
for (p[2] = 1; p[2] <= 5; p[2]++)
{
for (p[3] = 1; p[3] <= 5; p[3]++)
{
for (p[4] = 1; p[4] <= 5; p[4]++) //五层循环遍历
{
//这里是五个人的描述,由于比较表达式只有0和1两个结果,如果要两个条件有且只有一个为真,则可以用比较表达式的值总和为1的方式直接判定。别忘了还要判定不能并列。
if ((p[1] == 2) + (p[0] == 3) == 1 && //B第二,我第三
(p[1] == 2) + (p[4] == 4) == 1 && //我第二,E第四
(p[2] == 1) + (p[3] == 2) == 1 && //我第一,D第二
(p[2] == 5) + (p[3] == 3) == 1 && //C最后,我第三
(p[4] == 4) + (p[0] == 1) == 1 && //我第四,A第一
checkData(p) //不能并列
)
{
for (int i = 0; i < 5; i++)
{
printf("%d ", p[i]);
}
putchar('\n');
}
}
}
}
}
}
return 0;
}
改进一:
检查是否重复的过程,我们是用一个数组来做的,实际每个标签只有0和1两种可能,没必要一定要用数组做,可以考虑用一个位来做(哈希中的位图),代码如下:
int checkData(int *p)
{
char tmp = 0;
int i;
for (i = 0; i < 5; i++)
{
tmp |= 1 << p[i];
//tmp每次或上一位1,p[i]如果是1~5都有,则1<<1到1<<5都或上的结果将会是00111110,如果有并列,则一定会至少却其中一个1,结果就不会是00111110,所以可以判断tmp最终的结果是不是这个数字来判断有没有重复。
}
return tmp == 0x3E;
}
改进二:
循环代码又长又难看,可以考虑改成递归:
void diveRank(int * p, int n)
{
if(n >= 5) //此时的n是用来控制循环层数的。
{
if ((p[1] == 2) + (p[0] == 3) == 1 && //B第二,我第三
(p[1] == 2) + (p[4] == 4) == 1 && //我第二,E第四
(p[2] == 1) + (p[3] == 2) == 1 && //我第一,D第二
(p[2] == 5) + (p[3] == 3) == 1 && //C最后,我第三
(p[4] == 4) + (p[0] == 1) == 1 && //我第四,A第一
checkData(p)) //查重
{
for (int i = 0; i < 5; i++)
{
printf("%d ", p[i]);
}
putchar('\n');
}
return;
}
for(p[n] = 1; p[n] <= 5; p[n]++)
{
diveRank(p, n + 1); //通过递归模拟多层循环,每进一次递归相当于进了一层新的循环。
}
}
int main()
{
int p[5];
diveRank(p, 0);
return 0;
}
改进三:
以上的方法只是让代码简单了点,但还是需要55次比较,而如果本来就是做1~5的排列组合的话只需要5!次比较,能极大的减少遍历所需的次数(复杂度由O(nn)降低为O(n!)),那是不是可以用一个递归完成对1~5的全排列呢?当然是可以的,所以我们可以进一步优化遍历的方式,将遍历用的递归程序改成这样:
#include <stdio.h>
void swapArgs(int * a, int * b) //交换函数
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
void diveRank(int * p, int n)
{
if(n >= 5) //此时的n也是用来控制循环层数的。
{
if ((p[1] == 2) + (p[0] == 3) == 1 && //B第二,我第三
(p[1] == 2) + (p[4] == 4) == 1 && //我第二,E第四
(p[2] == 1) + (p[3] == 2) == 1 && //我第一,D第二
(p[2] == 5) + (p[3] == 3) == 1 && //C最后,我第三
(p[4] == 4) + (p[0] == 1) == 1) //我第四,A第一
//由于此时是执行的全排列,所以查重也省了。
{
for (int i = 0; i < 5; i++)
{
printf("%d ", p[i]);
}
putchar('\n');
}
return;
}
int i;
for(i = n; i < 5; i++) //这个递归方式就完成了对1~5的全排列,方法是从后向前不停的执行交换。可以参考改进二和原代码,将这个递归程序写回成循环后,可以更好的理解。
{
swapArgs(p + i, p + n);
diveRank(p, n + 1);
swapArgs(p + i, p + n);
}
}
int main()
{
int p[5] = { 1, 2, 3, 4, 5 }; //当然由于是全排列,所以初值必须给好。
diveRank(p, 0);
return 0;
}
至此,遍历速度上达到了一个新的高度,这种遍历大大减少了遍历的次数,极大的提升了效率。
💒6.小乐乐改数字
🌟6.1题目描述
🌻6.2思路
每次拿到数的最后一位,判断是奇数还是偶数,如果是奇数改为1,如果是偶数改为0.
🍅6.3代码演示
#include <stdio.h>
#include <math.h>
int main()
{
int n=0;
int sum=0;
scanf("%d",&n);
int i=0;
while(n)
{
//n的最后一位
int m=n%10;
//如果是奇数
if(m%2==1)
m=1;
//偶数
else
m=0;
//123
//第一次:sum=3*10^0=3
//第二次:sum=2*10^1+3=23
//第三次:sum=1*10^2+23=123
sum+=m*pow(10,i);
i++;
n/=10;
}
printf("%d",sum);
return 0;
}
💒7.小乐乐走台阶
🌟7.1题目描述
🌻7.2思路
🍅7.3 代码演示
#include <stdio.h>
int fib(int n)
{
if(n<=2)
{
return n;
}
else
{
return fib(n-1)+fib(n-2);
}
}
int main()
{
int n=0;
scanf("%d",&n);
int ret=fib(n);
printf("%d",ret);
return 0;
}