相关文章:
C语言——基础查漏补缺(一):超长文帮你理清一些概念
C语言——基础查漏补缺(二):《C程序设计试题汇编》应试概念总结
C语言——基础查漏补缺(三):谭浩强红书刷题笔记大杂烩
这是《挑战程序设计竞赛(第二版)》前两章的学习笔记,包含了贪心法、动态规划、DFS和BFS等,自认为是经典的基础算法补充学习资料。
第一章的热身
1,三角形
// 三角形,输入n,输入n个数(备选边长)
// 输出最大三角形边长,如果无法组成三角形就输出0
// 算法:暴搜
#include<stdio.h>
#define maxn 105
int max(int a, int b){
if(a >= b)return a;
else return b;
}
// 判断三条边是否能组成三角形
int judge(int a, int b, int c){
int m = max(a, max(b, c));
int len = a + b + c;
int remain = len - m;
if(remain > m)return 1;
else return 0;
}
int main(){
int a[maxn], n;
int len = 0, temp;
scanf("%d", &n);
for(int i = 0;i < n;i++){
scanf("%d", a + i);
}
for(int i = 0;i < n;i++)
for(int j = i + 1;j < n;j++)
for(int k = j + 1;k < n;k++){
if(judge(a[i], a[j], a[k])){
temp = a[i] + a[j] + a[k];
len = (temp > len) ? temp : len;
}
}
printf("%d\n", len);
return 0;
}
2,蚂蚁爬杆
// POJ 1852 蚂蚁爬杆
// 精髓在于认识到蚂蚁相遇折回相当于相遇穿过
#include<stdio.h>
#define maxn 105
int min(int a, int b){
if(a <= b)return a;
else return b;
}
int max(int a, int b){
if(a >= b)return a;
else return b;
}
int main(){
int L, n, a[maxn];
int minres = 0, maxres = 0;
scanf("%d %d", &L, &n);
for(int i = 0;i < n;i++)
scanf("%d", a + i);
// 选择最短时间
for(int i = 0;i < n;i++){
minres = max(minres, min(a[i], L - a[i]));
}
// 选择最长时间
for(int i = 0;i < n;i++){
maxres = max(maxres, max(a[i], L - a[i]));
}
printf("%d\n", minres);
printf("%d\n", maxres);
return 0;
}
3,二分搜索解决抽签问题
// 抽签问题 输入n和m,输入n个数,如果存在4个数
// 的和是m,则输出yes,否则输出no
// 本来是要用四个for嵌套,改进方法就是将最内层的for变成二分查找即可
#include<stdio.h>
#define maxn 105
// 二分查找,找到返回1,找不到返回0
int search(int a[], int start, int end, int x){
if(start > end)return 0;
int mid = (start + end) / 2;
if(a[mid] == x)return 1;
else if(a[mid] > x)
return search(a, start, mid - 1, x);
else
return search(a, mid + 1, end, x);
}
int main(){
int n, m, a[maxn];
scanf("%d %d", &n, &m);
for(int i = 0;i < n;i++)
scanf("%d", a + i);
for(int i = 0;i < n;i++)
for(int j = 0;j < n;j++)
for(int k = 0;j < n;j++){
// 本来这是最内层的for,现改成二分查找即可
if(search(a, 0, n - 1, m - a[i] - a[j] - a[k])){
printf("yes\n");
return 0;
}
}
printf("no\n");
return 0;
}
第二章经典基础算法学习
DFS和BFS
☆1,用DFS解决部分和问题
// 部分和问题v1.0 要求用dfs。
// 输入n,输入n个数,输入k
// 如果有若干个数之和为k,输出yes,反之输出no
#include<stdio.h>
int a[105], n, k;
// 前i项a[0]-a[i-1]已经得到sum的同时,能否满足最终得到k
int dfs(int i, int sum){
if(i == n)
return sum == k;
// 加上a[i]
if(dfs(i + 1, sum + a[i]))
return 1;
// 不加上a[i]
if(dfs(i + 1, sum))
return 1;
return 0;
}
int main(){
scanf("%d", &n);
for(int i = 0;i < n;i++)
scanf("%d", a + i);
scanf("%d", &k);
if(dfs(0, 0) == 1)
printf("yes\n");
else
printf("no\n");
return 0;
}
2,DFS数水洼
(输入N,M,输入NM的矩阵图形;输出水洼的数量)
// DFS数水洼问题。输入n和m,输入nm大小矩阵,
// w代表有水,.代表没水;输出水洼数量
#include<stdio.h>
int n, m;
char field[105][105];
// 从x行y列开始进行dfs
void dfs(int x, int y){
// 先将当前点的水消去
field[x][y] = '.';
for(int dx = -1;dx <= 1;dx++)
for(int dy = -1;dy <= 1;dy++){
int nx = x + dx, ny = y + dy;
// if(field[nx][ny] == 'w'
// && nx < n && nx >= 0
// && ny < m && ny >= 0){
// dfs(nx, ny);
// }
if(nx < n && nx >= 0
&& ny < m && ny >= 0
&& field[nx][ny] == 'w'){
dfs(nx, ny);
}
}
}
int main(){
int count = 0;
scanf("%d %d", &n, &m);
getchar();
for(int i = 0;i < n;i++){
for(int j = 0;j < m;j++){
field[i][j] = getchar();
}
getchar();
}
for(int i = 0;i < n;i++)
for(int j = 0;j < m;j++){
if(field[i][j] == 'w'){
dfs(i, j);
count++;
}
}
printf("%d\n", count);
return 0;
}
注意上述代码中注释掉的if语句,如果那样做可能会造成数组越界,因为if中有多个判断的时候,会从左至右进行判断。
3,☆☆BFS迷宫最短路径
输出从起点到终点的最短路径(上图是22)
数据结构:和地图相同大小的二维整型数组,存储开始结点到当前结点的距离,如果结点没有被访问过,则存储INF。
算法描述:
- 将起点入栈;
- 如果栈不空,则出栈,如果当前结点是终点,则停止算法,输出起点到当前结点的距离;如果当前结点不是终点,则依次遍历它的四个方向的邻点,如果邻点没有被访问过,且它不是墙壁,则入栈,返回此步起点。
贪心法
1,硬币问题
// 硬币问题, 分别输入1、5、10、50、100、500元硬币数量
// 输入A,输出支付A一共需要的最少硬币数量,假设一定有一种办法能刚好付清
// 算法:尽可能多用大面值即可
#include<stdio.h>
int min(int a, int b){
return (a < b) ? a : b;
}
int main(){
int c[6], A, count = 0;
int v[6] = {1, 5, 10, 50, 100, 500};
for(int i = 0;i < 6;i++)
scanf("%d", c + i);
scanf("%d", &A);
for(int i = 5;i >= 0;i--){
int t = min(A / v[i], c[i]);
A = A - t * v[i];
count += t;
}
printf("%d\n", count);
return 0;
}
2,区间调度问题
// 区间调度问题,输入n,输入n个数是n个工作开始时间
// 再输入n个数,是n个工作结束时间,输出最多参与几个工作
// 算法:可选的工作中,每次选择结束时间最早的工作
#include<stdio.h>
#define maxn 105
int s[maxn], t[maxn], n;
// 按照各个工作的结束时间进行排序
void sort(){
int temp;
for(int i = 0;i < n - 1;i++)
for(int j = 0;j < n - 1 - i;j++){
if(t[j] < t[j + 1]){
temp = t[j];
t[j] = t[j + 1];
t[j + 1] = temp;
temp = s[j];
s[j] = s[j + 1];
s[j + 1] = temp;
}
}
}
int main(){
int flag[maxn], count = 0;
scanf("%d", &n);
for(int i = 0;i < n;i++)
scanf("%d", s + i);
for(int i = 0;i < n;i++)
scanf("%d", t + i);
sort();
for(int i = 0;i < n;i++)
flag[i] = 0; // 值为1代表已经被选择(或者不能选择)
for(int i = n - 1;i >= 0;i--){
if(flag[i] == 1)
continue;
// 如果可以选择,才会运行到这里
count++;
flag[i] = 1;
// 将不能和i对应工作同时选择的工作标注为不可选
for(int j = i - 1;j >= 0;j--){
if(s[j] < t[i])
flag[j] = 1;
}
}
printf("%d\n", count);
return 0;
}
3,字典序最小问题
// 字典序最小问题。输入n,输入长度为n的字符串,
// 每次要么从首部要么从尾部取一个字母,放到新字符串(初始为空)
// 的末尾,需要使新字符串最小。输出新字符串
比如说输入
6
ACDBCB
ABCBCD
算法:只需要比较首尾谁小即可,如果遇到一样的情况,比如上述输入,剩下CDBC
时,只需要将此子字符串和它自己的反序比较一下即可,它的字典序更小,就选首部,如果它的反序字典序更小,就选尾部。
#include<stdio.h>
#include<stdlib.h>
#define maxn 105
int n;
char c[maxn];
// 从start到end的子字符串,正序比反序字典序小则返回1
int compare(char c[], int start, int end){
for(int i = start, j = end;i <= end;i++, j--){
if(c[i] < c[j])return 1;
if(c[i] > c[j])return 0;
}
return 0;
}
int main(){
int s, t;
scanf("%d", &n);
getchar(); // 吸收回车
char * res = (char *)malloc(sizeof(char));
for(int i = 0;i < n;i++)
scanf("%c", c + i);
s = 0;
t = n - 1;
for(int i = 0;i < n;i++){
if(c[s] < c[t]){
res[i] = c[s];
s++;
}
else if(c[s] > c[t]){
res[i] = c[t];
t--;
}
else{
if(compare(c, s, t) == 1){
res[i] = c[s];
s++;
}
else{
res[i] = c[t];
t--;
}
}
}
for(int i = 0;i < n;i++)
printf("%c", res[i]);
printf("\n");
free(res);
return 0;
}
4,标记点问题
// 标记点问题。
// 输入n,输入n个点的坐标,输入R,对若干点标记。要求每
// 个点要么自己被标记,要么距离标记点R以内,输出标记点个数
算法如图,一目了然:
#include<stdio.h>
#define maxn 105
int n, r, c[maxn];
int main(){
int temp, count = 0, i;
scanf("%d %d", &n, &r);
for(int i = 0;i < n;i++)
scanf("%d", c + i);
i = 0;
while(i < n){
// 存储没标记也被标记点覆盖的最左结点坐标
temp = c[i];
while(i < n && c[i] - temp <= r)i++;
// 给点i-1添加标记
temp = c[i - 1];
while(i < n && c[i] - temp <= r)i++; // 跳出循环时,i是不能被刚标记的点覆盖的最左结点
count++;
}
printf("%d\n", count);
return 0;
}
5,哈夫曼的应用(总是选最小的两个)
#include<stdio.h>
#define maxn 105
int n, a[maxn];
void sort(int a[], int s, int t){
int flag = 0, i, j, temp;
for(i = 0;i < t - s;i++){
for(j = s;j <= t - 1 - i;j++){
if(a[j] > a[j - 1]){
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
flag = 1;
}
}
if(flag == 0)break;
}
}
int main(){
int cost = 0;
scanf("%d", &n);
for(int i = 0;i < n;i++)
scanf("%d", a + i);
for(int i = 0;i < n - 1;i++){
sort(a, i, n - 1);
cost += (a[i] + a[i + 1]);
a[i + 1] += a[i];
}
printf("%d\n", cost);
return 0;
}