喝汽水
20元->20空瓶
20空瓶->送10瓶
10瓶->送5瓶
5瓶-> 送2瓶+1瓶(存在)
3瓶->送1瓶+1瓶(存在)
2瓶->1瓶
总共喝了20+19瓶
总共喝了的数量分为两部分
- 实实在在买的
- 用空瓶子换的
int main()
{
int money;
int total;
int empty;
scanf("%d", &money);
//直接买的
total = money;
empty = total;
//送的
while (empty > 1)
{
total += empty / 2;
empty = empty / 2 + empty % 2;
}
printf("%d\n", total);
return 0;
}
调整奇数偶数顺序
奇数在数组中前半部分,偶数在数组中后半部分,可以通过
双指针
来解决
left指向首元素,right指向尾元素,当left指向的元素不是偶数,left向后挪动直到指向偶数,right指向的元素不是奇数,right向前挪动直到right指向奇数
交换left和right指向的元素,重复上述动作直到left>=right
void Move(int* arr, int sz)
{
int left = 0;
int right = sz - 1;
while (left < right)
{
//让left指向偶数
while (arr[left] % 2 == 1 && left < right) left++;//循环条件保证数组不会因为全是奇数而越界
//让right指向奇数
while (arr[right] % 2 == 0 && left < right) right--;//循环条件保证数组不会因为全是奇数而越界
//交换
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
}
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7 ,8 };
int sz = sizeof arr / sizeof arr[0];
Move(arr, sz);
for (int i = 0; i < sz; i++)
printf("%d ", arr[i]);
return 0;
}
猜名次
每个人都可能有5种状态,第一、第二、第三、第四、第五
每位选手说对了一半可以用条件表达式x+y==1(x、y表示事件)来表示
int main()
{
int a, b, c, d, e;
for (a = 1; a <= 5; a++)
{
for (b = 1; b < 5; b++)
{
for (c = 1; c <= 5; c++)
{
for (d = 1; d <= 5; d++)
{
for (e = 1; e <= 5; e++)
{
//保证a五人排名不重复
if (a * b * c * d * e == 120)
{
//5个人都说对一半
if ((b == 2) + (a == 3) == 1
&& (b == 2) + (e == 4) == 1
&& (c == 1) + (d == 2) == 1
&& (c == 5) + (d == 3) == 1
&& (e == 4) + (a == 1) == 1)
{
printf("a=%d,b=%d,c=%d,d=%d,e=%d\n", a, b, c, d, e);
return 0;
}
}
}
}
}
}
}
return 0;
}
猜凶手
从凶手的状态考虑有4种,凶手是a,凶手是b,凶手是c,凶手是d,三个人说真话可以表示为p+q+x+y==3
int main()
{
char killer;
for (killer = 'a'; killer <= 'd'; killer++)
{
if ((killer != 'a') + (killer == 'c') + (killer == 'd') + (killer != 'd') == 3)
{
printf("%c\n", killer);
break;
}
}
return 0;
}
从4个人的状态考虑每个人都有是凶手和不是凶手两种状态,因此还可以这么写
int main()
{
char a, b, c, d;
for (a = 0; a <= 1; a++)
{
for (b = 0; b <= 1; b++)
{
for (c = 0; c <= 1; c++)
{
for (d = 0; d <= 1; d++)
{
if ((a != 1) + (c == 1) + (d == 1) + (d != 1) == 3)
{
printf("%d %d %d %d", a, b, c, d);//1为凶手
goto lip;
}
}
}
}
}
lip:;
}
字符串左旋
可以暴力遍历,循环k次,每次把最左边的字符移动到最右边,比较简单,这里不再赘述
想要左旋转字符串,可以通过以下三步完成
- 逆序前k个字符
- 逆序后len-k个字符
- 逆序整个字符串(不包括结束符)
#include <string.h>
void Reverse(char* start, char* end)
{
int left = 0;
int right = end - start;
while (left < right)
{
char tmp = start[left];
start[left] = start[right];
start[right] = tmp;
left++;
right--;
}
}
char* LeftMoveStr(char* str, int k)
{
k %= strlen(str);
Reverse(str, str + k - 1);
Reverse(str + k, str + strlen(str) - 1);
Reverse(str, str + strlen(str) - 1);
return str;
}
int main()
{
char str[] = "abcdefgh";
int k;
scanf("%d", &k);
printf("%s\n", LeftMoveStr(str, k));
return 0;
}
字符串旋转结果
本题当然可以将所有旋转后的结果放到一个数组里,然后进行查找,但是这种做法既不好操作,也太费事,但是这题有一个很简单的做法:
其实ABCDE无论怎么旋,旋转后的所有结果,都包含在了ABCDEABCD这个字符串里了。
所以做法很简单,只需要将原字符串再来一遍接在后面,然后找一找待查找的字符串是不是两倍原字符串的子集即可。
int IsLeftRotate(char* str, char* find)
{
strncat(str, str, strlen(str));//注意这里不要使用strcat拼接自己
return strstr(str, find) == NULL ? 0 : 1;
}
int main()
{
char str1[20] = "abcdef";
char find[8] = "cdefab";
if (IsLeftRotate(str1, find))
{
printf("Yes\n");
}
else printf("No\n");
return 0;
}
杨氏矩阵
我们仔细分析,不难发现,对于杨氏矩阵老说,右上角和左下角的元素是有特点的。右上角的元素是一行中最大的,一列中最小的。左下角的元素是一行中最小的,是一列中最大的。所以我们可以从右上角或者左下角开始查找。比如:从右上角开始查找的时候,右上角的元素比我们要查找元素小,我们就可以去掉右上角元素所在的这一行;右上角的元素比我们要查找的元素大,我们就可以去掉右上角元素所在的这一列。然后依然找右上角的元素继续和要查找的元素与比较。这样每一次比较去掉一行或者去掉一列。这个查找效率是高于遍历数组元素的,所以时间复杂度是小于O(N),也满足题目要求。
int FindKeyFromArr(int arr[][3], int* row, int* col, int key)
{
int x = 0;
int y = *col - 1;
//循环终止条件
while (x <= row - 1 && y >= 0)
{
if (arr[x][y] < key) x++;
else if (arr[x][y] > key) y--;
else
{
//返回找到的坐标
*row = x + 1;
*col = y + 1;
return 1;
}
}
return 0;
}
int main()
{
int arr[3][3] = { {1,2,3}, {4,5,6}, {7, 8, 9} };
int key;
int x = 3; int y = 3;
scanf("%d", &key);
int ret = FindKeyFromArr(arr, &x, &y, key);
if (1 == ret)
{
printf("找到啦!下标为(%d,%d)\n", x, y);
}
else printf("没找到!\n");
return 0;
}