2023/10/16 周一
贪心算法
对问题求解时不从整体最优上加以考虑,只做出在某种意义上的局部最优解。通俗来讲,就像你去吃自助餐都会挑最贵的吃,而不考虑所吃的总体营养是否搭配均衡,会不会引发胰腺炎等健康问题。当下的任务就是挑最贵的吃。
最典型案例的就是背包问题
腾讯有这么一个游戏叫暗区突围,成功携带物资从地图撤离是你的最终目标。当你带了一个背包进入对局时,你搜索到了很多杂物,背包装不下了。你应该怎么挑选杂物,让带出的收益最大呢。那当然是挑贵的带!
但是,贵的物资重量和占背包体积都不一样,比如说15000的钢筋剪,占6格空间,20kg重。带上跑不快,容易被人发现被打死(不能超重)。这时候贪心算法就发挥作用了,先按重量排序,算出每件物品一格空间多少钱。这里有两种解法,一是尽可能塞满背包格子,二是不超重,不必塞满格子。
蓝桥杯练习1 删数
【问题描述】
有一个长度为n(n <= 240)的正整数,去掉其中任意s个数字后,将剩余的数保持原来的次序组成一个新的正整数。对给定的n和s,编程寻找一种方案,使得剩下的数字组成的新数最小
【输入格式】
输入两行,正整数n和一个整数s
【输出格式】
一个数字,表示最后剩下的最小数。
【输入样例】
175438
4
【输出样例】
13
【解题思路】
没有使用贪心:
在n中找到最大值,然后删除
使用贪心:
数组中if判断 a[i]>a[i+1],删除 a[i],数字前移
【参考代码】
没有使用贪心:
#include <iostream>
using namespace std;
#include <algorithm>
int arr[20] = { 0 };
void getNumber(int num)//取数,放在数组中
{
int i = 0;
while (num)
{
int temp;
temp = num % 10;
arr[i] = temp;
num /= 10;
i++;
}
}
int deleteNum(int num)
{
for (int i = 0; sizeof(arr) / sizeof(int); i++)
{
if (arr[i] == num)
{
arr[i] = 0;
return 0;
}
}
}
int main()
{
int n, s;
cin >> n;
cin >> s;
getNumber(n);
//寻找最大值
for (int i = 0; i < s; i++)
{
int max = 0;
for (int j = 0; j < sizeof(arr) / sizeof(int); j++)
{
if (arr[j] > max)
max = arr[j];
}
deleteNum(max);
}
//排序后输出
sort(arr, arr + 20);
for (int i = 0; i < 20; i++)
{
if (arr[i] != 0)
cout << arr[i];
}
}
使用贪心:
#include <iostream>
using namespace std;
//使用贪心
int main()
{
char str[20];
int s;
cin >> str;
cin >> s;
for (int j = 0; j < s; j++)
for (int i = 0; i < sizeof(str) / sizeof(char); i++)
{
if (str[i] > str[i + 1])
{
str[i] = str[i + 1];
}
}
cout << str;
}
简便了很多
【输出结果】
2023/10/17 周二
蓝桥杯练习2 翻硬币
【题目描述】
小明正在玩“翻硬币”的游戏。桌上放着排成一排的若干硬币。我们用“*”表示正面用“o”表示反面(是小写字母,不是零)
例如,可能的情形是**oo***oooo
如果同时翻转左边的两个硬币,则变为 oooo***ooo0。
现在的问题是:如果已知初始状态和要达到的目标状态,且每次只能同时翻转相邻的两个硬币,那么对于特定的局面,最少要翻动多少次呢?
我们约定:把翻动相邻的两个硬币叫作一步操作。
【输入格式】
两行等长的字符串,分别表示初始状态和要达到的目标状态,每行的长度小于 1000。
【输出格式】
一个整数,表示最小操作步数
【样例输入】
*o**o***o***
*o***o**o***
【样例输出】
1
【题目分析】
贪心算法只看局部,局部最优解
两行字符相同就去掉,不同的情况是下面四种
做完啦!
【参考代码】
#include <iostream>
using namespace std;
#include <string>
int main()
{
char str1[15];
char str2[15];
int cnt = 0;
cin >> str1;
cin >> str2;
//去掉相同项
for (int i = 0; i < sizeof(str1) / sizeof(char); i++)
{
if (str1[i] == str2[i])
{
str1[i] = 0;
str2[i] = 0;
}
}
//查找是否符合情况
for (int i = 0; i < sizeof(str1) / sizeof(char); i++)
{
if ((str1[i] == 'o' && str1[i + 1] == 'o') &&
(str2[i] == '*' && str2[i + 1] == '*'))
cnt++;
if ((str1[i] == 'o' && str1[i + 1] == '*') &&
(str2[i] == '*' && str2[i + 1] == 'o'))
cnt++;
if ((str1[i] == '*' && str1[i + 1] == 'o') &&
(str2[i] == 'o' && str2[i + 1] == '*'))
cnt++;
if ((str1[i] == '*' && str1[i + 1] == '*') &&
(str2[i] == 'o' && str2[i + 1] == 'o'))
cnt++;
}
cout << cnt;
}
【运行结果】
2023/10/18 周三
蓝桥杯练习3 分糖果
【题目描述】
有 n个小朋友坐成一圈, 每人有 a[]个糖果。
每人只能给左右两边的人传递糖果。
每人每次传递一个糖果的代价为 1。
求使所有人获得均等糖果的最小代价。
【输入格式】
第一行输入一个正整数 n, 表示小朋友的人数。
接下来的 n行, 每行一个整数 a[i], 表示第i个小朋友初始得到的糖果颗数
【输出格式】
输出一个整数,表示最小代价。
【数据范围】
1≤n≤1000000,0<a[i]<2X10 ,数据保证一定有解
【样例输入】
4
1
2
5
4
【样例输出】
4
【题目分析】
利用贪心算法,只考虑相邻两个数的情况。
考虑小朋友是坐成一个圈的,所以a[0]和a[n-1]可以互相交换糖果
【参考代码】
#include <iostream>
using namespace std;
int a[10] = { 0 };
int main()
{
int n, i;//小朋友个数
int cnt = 0;
cin >> n;
//input
for (i = 0; i < n; i++)
{
cin >> a[i];
}
for (i = 0; i < n; i++)
{//相邻两个数之间进行加减
if (a[i - 1] < a[i] )
{
a[i]--;
a[i - 1]++;
cnt++;
}
else if (a[i] > a[i + 1] )
{
a[i]--;
a[i + 1]++;
cnt++;
}
//边界(数组头尾)之间的情况
else if (i == n - 1)
{
if (a[i] > a[0])
{
a[i]--;
a[0]++;
cnt++;
}
else
{
a[0]--;
a[i]++;
cnt++;
}
}
}
cout << cnt;
}
【运行结果】
4
2023/10/19 周四
斐波那契数列(栈实现)
【代码分析】
先放第一个,第二个进去栈
取出这两个数,加起来得a3,栈顶元素放a1,a3传给a2,再放进去
取出这两个数,加起来得a3,栈顶元素放a1,a3传给a2,再放进去(重复步骤)
【参考代码】
主程序
#include <stdio.h>
typedef int Elemtype;
#define MaxSize 5
#include "Stack.h"
int main()
{
int a1 = 1, a2 = 1, a3;
int n, i;
Stack s;
StackInitiate(&s);
//输入第n个值的位置
scanf("%d", &n);
for(i = 0; i < n - 2; i++)
{
push(&s, a1);
push(&s, a2);
a3 = a1 + a2;
pop(&s, &a1);
pop(&s, &a2);
a2 = a3;
}
printf("%d\n", a3);
}
Stack.h文件
//typedef int Elemtype;
typedef struct Stack
{
Elemtype stack[MaxSize];
int top; //栈顶下标值
}Stack;
//栈初始化
void StackInitiate(Stack *S)
{
S->top = 0;
}
//返回栈的大小
int StackSize(Stack S)
{
return S.top;
}
//判断栈是否为空
int isEmpty(Stack S)
{
if(S.top <= 0) //空栈的情况
return 0;
else
return 1;
}
//清空栈
void ClearStack(Stack *S)
{
S->top = 0;
}
//入栈
int push(Stack *S, Elemtype x)
{
if (S->top >= MaxSize)
{
printf("栈已满无法插入!\n");
return 0;
}
else
{
S->stack[S->top] = x;
S->top++;
return 1;
}
}
//出栈
int pop(Stack *S, Elemtype *d)
{
if(S->top <= 0)
{
printf("栈已空无数据元素出栈!\n");
return 0;
}
else
{
S->top--; //先减后弹出
*d = S->stack[S->top]; //将栈顶元素值赋给*d
return 1;
}
}
//取栈顶元素
int getTopElem(Stack S, Elemtype *d)
{
if(S.top <= 0)
{
printf("栈已空!\n");
return 0;
}
else
{
*d = S.stack[S.top - 1]; //将栈顶元素值赋给*d
return 1;
}
}
【运行结果】
2023/10/20 周五
蓝桥杯练习4 推销员
【题目描述】
阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户。螺丝街一共有N家住户,第i家住户到入口的距离为S_i米。由于同一栋房子里可以有多家住户,所以可能有多家住户与入口的距离相等。阿明会从入口进入,依次向螺丝街的X家住户推销产品,然后再原路走出去。
阿明每走1米就会积累1点疲劳值,向第i家住户推销产品会积累A_i点疲劳值。阿明是工作狂,他想知道,对于不同的X,在不走多余的路的前提下,他最多可以积累多少点疲劳值。
【输入格式】
第一行有一个正整数N,表示螺丝街住户的数量。
接下来的一行有N个正整数,其中第i个整数S_i表示第i家住户到入口的距离。数据保证S_1≤S_2≤…≤S_n<10^8
接下来的一行有N个正整数,其中第i个整数A_i表示向第ii户住户推销产品会积累的疲劳值。数据保证A_i<1000。
【输出格式】
输出N行,每行一个正整数,第i行整数表示当X=i时,阿明最多积累的疲劳值。
【样例输入1】
5
1 2 3 4 5
1 2 3 4 5
【样例输出1】
15
19
22
24
25
【样例输入2】
5
1 2 2 4 5
5 4 3 4 1
【样例输出2】
12
17
21
24
27
【题目分析】
X=1:向最里面住户5推销,往返走路的疲劳值为5+5,推销的疲劳值为5,总疲劳值为15。
X=2:向前一位住户4推销,推销的疲劳值为4,总疲劳值为19。
X=3:向前一位住户3推销,推销的疲劳值为3,总疲劳值为22。
X=4:向前一位住户2推销,推销的疲劳值为2,总疲劳值为24。
X=5:向前一位住户1推销,推销的疲劳值为1,总疲劳值为25。
走到最后开始,一点一点往前推,往返走路的疲劳值就先算上去。
样例2就不一样了
路口就有5个住户,是不是要先到呢
题目说在不走多余的路的前提下,他最多可以积累多少点疲劳值。
那就往最里面走,而且要是比较多的住户。
【参考代码】
#include <iostream>
using namespace std;
int dis[10] = { 0 };//住户离入口的距离
int house[10] = { 0 };//住户数量
//返回该住户离入口的距离
int searchMaxDistance(int num, int N)
{
int i;
for (i = 10; i > 0; i--)
{
if (num == house[dis[i]])
break;
}
return dis[i];
}
//贪心算法,去最远最多住户的地方去推销
//只用一次,找远+多的
int maxGreedy(int house[], int N)
{
int i;
int max = house[0];
for (i = N / 2; i < 10; i++)
{
if (house[i] > max)
max = house[i];
}
return max;
}
int max(int house[], int N)
{
int i;
int max = house[0];
for (i = 0; i < 10; i++)
{
if (house[i] > max)
max = house[i];
}
return max;
}
int main()
{
int N;
cin >> N;
for (int i = 1; i <= N; i++)
{
cin >> dis[i];
//dis[i] = i;
}
for (int i = 1; i <= N; i++)
{
cin >> house[i];
//house[i] = i;
}
int temp, d1, d2, res = 0;
//多的住户
temp = maxGreedy(house, N);
//远的距离
d1 = searchMaxDistance(temp, N);//max的距离
res = 2 * d1 + temp;
cout << res << endl;
house[d1] = 0; dis[d1] = 0;
temp = max(house, N);
d2 = searchMaxDistance(temp, N);
if(d2 > d1)//判断小于d1,小于就不用加距离了
res += 2 * d2 + temp;
else
res += temp;
cout << res << endl;
house[d2] = 0; dis[d2] = 0;
temp = max(house, N);
d2 = searchMaxDistance(temp, N);
if (d2 > d1)//判断小于d1,小于就不用加距离了
res += 2 * d2 + temp;
else
res += temp;
cout << res << endl;
house[d2] = 0; dis[d2] = 0;
temp = max(house, N);
d2 = searchMaxDistance(temp, N);
if (d2 > d1)//判断小于d1,小于就不用加距离了
res += 2 * d2 + temp;
else
res += temp;
cout << res << endl;
house[d2] = 0; dis[d2] = 0;
temp = max(house, N);
//cout << temp << endl;
d2 = searchMaxDistance(temp, N);
if (d2 > d1)//判断小于d1,小于就不用加距离了
res += 2 * d2 + temp;
else
res += temp;
cout << res << endl;
house[d2] = 0; dis[d2] = 0;
}
【运行结果】
数据结构 数组的应用
1、设矩阵A、矩阵B为n阶对称矩阵,矩阵元素为整数类型,要求:
(1)若A、B采用压缩存储方式,请编写算法实现矩阵乘法运算C=A*B。请注意两个对称矩阵的乘积不一定是对称的。
(2)写一压缩矩阵的元素输出函数,要求按矩阵行列方式输出元素。
(3)以下面的数据为测试例子,编写一个主程序调用以上两个函数进行测试,输出矩阵A,B,C。以下两矩阵分别是矩阵A和矩阵B。
10 20 30 20 40 50 30 50 60 |
1 2 3 2 4 5 3 5 6 |
【输出结果】
【代码】
#include <stdio.h>
#include <malloc.h>
//创建
int **get2DArray(int row, int col)
{
int **a, i;
a = (int **) calloc(row, sizeof(int *));
for(i = 0; i < row; i++)
{
a[i] = (int *) calloc(col, sizeof(int));
}
return a;
}
//void print2DArray(int row, int col,)
//销毁
void destroy2DArray(int **a, int row)
{
int i;
for(i = 0; i < row; i++)
{
free(a[i]);
}
}
int main()
{
int n, i, j, k, row, col;
int **a1, **a2, **a3;
int b[6];
printf("输入阶数\n");
scanf("%d", &n);//输入阶数
row = col = n;
a1 = get2DArray(row, col);
printf("生成矩阵A,输入压缩矩阵的数\n");
for(i = 0; i < row; i++)
{
for(j = 0; j <= i; j++)
{
if( i == j )//关键点
scanf("%d", &a1[i][j]);
else
{
scanf("%d", &a1[i][j]);
a1[j][i] = a1[i][j];//关键点
}
}
}
// for(i = 0; i < row; i++)
// {
// for(j = 0; j < col; j++)
// scanf("%d", &a1[i][j], &a1[j][i]);
// a1[j][i] = a1[i][j];
// }
//输出矩阵
for(i = 0; i < row; i++)
{
for(j = 0; j < col; j++)
printf("\t%d", a1[i][j]);
printf("\n");
}
printf("生成矩阵B,输入压缩矩阵的数\n");
a2 = get2DArray(row, col);
for(i = 0; i < row; i++)
{
for(j = 0; j <= i; j++)//先填下半
{
if( i == j )//关键点
scanf("%d", &a2[i][j]);
else
{
scanf("%d", &a2[i][j]);
a2[j][i] = a2[i][j];//关键点
}
}
}
//输出矩阵
for(i = 0; i < row; i++)
{
for(j = 0; j < col; j++)
printf("\t%d", a2[i][j]);
printf("\n");
}
printf("\n");
printf("A * B =");
a3 = get2DArray(row, col);
for(i = 0; i < row; i++)
{
for(j = 0; j < col; j++)
{
for(k = 0; k < n; k++)//负责遍历当前位置行和列
{
a3[i][j] += a1[i][k] * a2[k][j];
}
//a3[i][j] += a1[i][j] * a2[j][i];
}
}
//输出矩阵
for(i = 0; i < row; i++)
{
for(j = 0; j < col; j++)
printf("\t%d", a3[i][j]);
printf("\n");
}
}
2023/10/21 周日
蓝桥杯练习5 排座位
【问题描述】
上课的时候总有一些同学会和前后左右的人交头接耳,这是令班主任十分头疼的一件事情。不过,班主任小雪发现了一些有趣的现象,当同学们的座次确定下来之后,只有有限的 D对同学在上课时会交头接耳。同学们在教室中坐成了 M行 N列,坐在第i行第i列的同学的位置是(i,j),为了方便同学们进出,教室中设置了 K 条横向的通道和 L条纵向的通道。于是,聪明的小雪想到了一个办法,这或许可以减轻上课时学生交头接耳的问题:她打算重新摆放桌椅,改变同学们桌椅之间通道的位置,因为如果一条通道隔开了两个会交头接耳的同学,那么他们就不会交头接耳了。请你帮忙给小雪编写一个程序,给出最好的通道划分方案,在该方案下,上课时交头接耳的学生对数是最少的。
【输入格式】
输入的第一行有 5 个用空格隔开的整数,分别是M、N、K、L、D。
接下来的 D行,每行有 4个用空格隔开的整数,第 i行的 4个整数 Xi、Yi、Pi、Qi表示坐在位置(Xi,Yi)与(Pi, Qi)的两个同学会交头接耳(输入保证他们前后或者左右相邻)。
输入数据保证最优方案的唯一性。
【输出格式】
输出共两行。
第一行包含 K 个整数 a1, a2, ..., ak,表示第 a 行和第 ai + 1行之间、第 a2 行和第a2 + 1行之间、···、第 ak 行和第 ak + 1行之间要开辟通道,其中 ai < ai + 1, 每两个整数之间用空格隔开(行尾没有空格)。
第二行包含 L 个整数 b1, b2, ..., bL,表示第 b1 列和第 b1 + 1列之间、第 b2 列和第b2 + 1列之间、···、第 bL 列和第 bL+1 列之间要开辟通道,其中 bi < bi+1, 每两个整数之间用空格隔开(行尾没有空格)。
【数据范围】
2<N,M<1000,0≤K<M,0<L<N,D<2000。
【样例输入】
4 5 1 2 3
4 2 4 3
2 3 3 3
2 5 2 4
【样例输出】
2
2 4
【题目分析】
数据分析后,就改这几个熊孩子的座位就行。二维数组什么的也不用建了,两个数组输出行和列
【参考代码】
#include <iostream>
using namespace std;
//题目:输入的5个数字,行max:4,列max:5,横向通道:1
//纵向通道:2,总通道:3。
int main()
{
int res_row[5] = {0}, res_col[5] = {0};
int i = 0, j = 0, k;
int rowmax, colmax; //行,列
int ver_access, par_access, sum_access; //垂直,平行通道, 总通道
cin >> rowmax >> colmax >> par_access >> ver_access >> sum_access;
int r1, r2, c1, c2;
for (k = 0; k < sum_access; k++)
{
cin >> r1 >> c1 >> r2 >> c2;
if (r1 == r2 && c1 < c2)//判断行是否相等
{
res_col[i] = c1;
i++;
}
else if (r1 == r2 && c1 > c2)//判断行是否相等
{
res_col[i] = c2;
i++;
}
if (c1 == c2 && r1 < r2)//判断列是否相等
{
res_row[j] = r1;
j++;
}
else if (c1 == c2 && r1 > r2)//判断列是否相等
{
res_row[j] = r2;
j++;
}
r1 = 0; r2 = 0; c1 = 0; c2 = 0;
}
for (i = 0; i < 5; i++)
{
if (res_row[i] != 0)
cout << res_row[i] << " ";
}
cout << "\n" ;
for (j = 0; j < 5; j++)
{
if (res_col[j] != 0)
cout << res_col[j] << " ";
}
}
【运行结果】