文章目录

  • 上课部分
    • 回顾:
    • 例1字典序最小问题
      • PS:
      • 颜老板版本
      • 更新版
    • 吐槽
    • 思路
    • AC代码
      • 安全版
      • 非安全,极简版
    • 思考:任意一串字符,删除其中的重复字符!
      • 题目描述
      • AC代码
        • 哈希表
        • 哈希表 写法二
        • ASCII码
        • set
      • 多个字符串的情况下
        • set
        • 哈希表
        • ASCII
    • 例2背包问题:
      • 颜老板版本
    • 吐槽
    • 思路
    • AC代码
  • 自学部分
    • 霍夫曼编码
    • 颜老板版本
    • 更新版
    • 第三届蓝桥杯【省赛试题8】密码发生器
      • 颜老板版本
    • 吐槽
      • 鸡蛋里找骨头
      • 蛋疼环节
    • 思路
    • AC代码

上课部分

回顾:

贪心算法的算法思想是简单的!
对问题求解的时候
每一步的选择(操作)都必须满足当前是最优解!
期望:按照上述规则,最终也能得到的结果就是问题的最优解!

问题开始的时候,是没有代入整个问题本身去考虑!
【开始遇见的问题,无法确定使用贪心算法是否能保证最后也是最优解!】

【分析中!思考如果使用贪心算法,不会对后面的计算以及结果的产生会有影响!】

分析!!!

例1字典序最小问题

问题描述:
给定长度为N的字符串S,
要构造一个长度为N字符串T。
T开始是一个空串,反复执行下列任意操作:
1)从S的头部删除一个字符,加到T的尾部;
2)从S的尾部删除一个字符,加到T的尾部;
目标是要构造字典序尽可能小的字符串T。

PS:

字典序是指从前到后比较两个字符串的大小的方法。
首先比较第1个字符,如果不同
则第1个字符较小的字符串更小,
如果相同则继续比较第2个字符…
反复继续,来比较整个字符串的大小。

输入 ACDBCB
输出

颜老板版本

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
int main()
{int n;char *s;int zb,yb;printf("请你输入一个字符串的长度:");scanf("%d",&n);getchar();s=(char *)malloc(n+1);printf("输入一个字符串:");gets(s);zb=0;yb=n-1;//*(s+?) 表示s中第?个位置上的字符的值 s[?]//谁小谁输出while(zb<=yb){int left=0; //left=0,表示左边字符小,left=1,表示左边字符大int i;for(i=0;zb+i<=yb;i++){if(*(s+zb+i) < *(s+yb))//左边小,左边的是该输出{left=0;break;}else if(*(s+zb+i) > *(s+yb))//左边大,右边的是该输出{left=1;break;}}if(left==1){putchar(*(s+yb));yb--;}else {putchar(*(s+zb));zb++;}}return 0;
}

更新版

吐槽

纯C代码写字符串真的是让我和小伙伴在台下看得一言难尽
建议用CPP,用String,用reverse
所以我们只理解做题思路,直接A

思路

从PS这一番话里我们知道是字符串前后向中间持续比较字符大小

(1)安全版
为了防止下标问题我们可以直接拆分
例如
A::ACDBCB t1
B::BCBDCA t2

给AB数组分别一个INDEX,在AB数组持续找最小,然后赋值给res数组
输出res答案即可
(2)极简版
需要注意下标处理和生成逻辑

AC代码

安全版

#include<iostream>
#include<algorithm>
#include<vector>using namespace std;
vector<char>ans;int main()
{string a;cin>>a;string b=a;reverse(b.begin(),b.end());int t1,t2,t3;t1=t2=t3=0;while(1){if(a[t1]<=b[t2])ans.push_back(a[t1++]),t3++;else ans.push_back(b[t2++]),t3++;if(t3==a.size())break;}for(int i=0;i<ans.size();i++)cout<<char(ans[i]);return 0;
}

非安全,极简版

需要注意下标处理和生成逻辑

#include<iostream>using namespace std;int main()
{string a;cin>>a;int l=0,r=a.size()-1;while(l<=r)if(a[l]<a[r])cout<<a[l++];else cout<<a[r--];return 0;
}

思考:任意一串字符,删除其中的重复字符!

题目描述

去除字符串中重复的字符

AC代码

哈希表

#include<iostream>
#include<string>
#include<unordered_map>using namespace std;unordered_map<char,int> map;int main()
{string s;cin>>s;for(int i=0;i<s.size();i++)map[s[i]]++;for(auto x:map)cout<<x.first;return 0;
}

哈希表 写法二

#include<iostream>
#include<string>
#include<unordered_map>using namespace std;unordered_map<char,int> map;int main()
{string s;cin>>s;for(int i=0;i<s.size();i++)map[s[i]]++;for(int i=0;i<'z';i++)if(map.count(char(i)))cout<<char(i);return 0;
}

ASCII码

#include<iostream>
#include<cstring>using namespace std;int main()
{string s;cin>>s;bool a['z'];for(int i=0;i<s.size();i++)if(!a[s[i]]){cout<<s[i];a[s[i]]=true;}return 0;
}

set

#include<iostream>
#include<set>using namespace std;int main()
{set<char>Set;string s;cin>>s;for(int i=0;i<s.size();i++)Set.insert(s[i]);for(char x:Set)cout<<x<<endl;return 0;
}

多个字符串的情况下

set

#include<iostream>
#include<set>using namespace std;int main()
{set<string>Set;int n;cin>>n;for(int i=0;i<n;i++){string tmp;cin>>tmp;Set.insert(tmp);}for(string x:Set)cout<<x<<endl;return 0;
}

哈希表

#include<iostream>
#include<string>
#include<unordered_map>using namespace std;unordered_map<string,int> map;int main()
{int n;cin>>n;for(int i=0;i<n;i++){string s;cin>>s;map[s]++;}for(auto x:map)cout<<x.first;return 0;
}

ASCII

用string单个的ASCII加起来,每个位子上乘位数如(10,100)组成新的ASCII下标数据,懒得写了

例2背包问题:

假如我们有一个可以装100kg物品的背包,我们有5种豆子,每种豆子的总量和总价值各不相同。为了让背包中所装的物品的总价值最大,我们如何选择装哪些豆子,每种装多少?【可以选择某种物品装部分】

颜老板版本

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<string.h>struct wp
{char name[5];double weight;double value;double price;
};struct select
{char name[5];double weight;double price;
};int n;void input(struct wp *p,int n)
{int i;for(i=0;i<n;i++){printf("输入第%d个物品的信息\n",i+1);printf("输入物品名称=");gets((p+i)->name);printf("输入物品总量kg=");scanf("%lf",&(p+i)->weight);printf("输入物品总价值=");scanf("%lf",&(p+i)->value);(p+i)->price=(p+i)->value/(p+i)->weight;getchar();}system("pause");
}void sort(struct wp *p,int n)
{struct wp t;int i,j;for(j=0;j<n-1;j++)for(i=0;i<n-1-j;i++){if(( (p+i)->price )<( (p+i+1)->price)){t=*(p+i);*(p+i)=*(p+i+1);*(p+i+1)=t;}}
}//进行背包问题的求解
int fun( struct wp *p,struct select *q,int n)
{double weight_sheng=100;int k=0;//准备存储数据到q中的位置变化int i;for(i=0;i<n;i++){if( (p+i)->weight <= weight_sheng){//说明当前第i个物品可以全部放入//当前p所指向的第i个物品名称、总量和单价存入q所指向的存储空间中strcpy((q+k)->name,(p+i)->name);(q+k)->weight=(p+i)->weight;(q+k)->price=(p+i)->price;k++;weight_sheng=weight_sheng-(p+i)->weight;}      else {strcpy((q+k)->name,(p+i)->name);(q+k)->weight=weight_sheng;(q+k)->price=(p+i)->price;weight_sheng=0;break;}}return k;
}void output(struct select *q,int k)
{int i;printf("物品    总量    单价\n");for(i=0;i<=k;i++){printf("%-8s",(q+i)->name);printf("%-8.2lf",(q+i)->weight);printf("%-8.2lf\n",(q+i)->price);}
}int main()
{struct wp *p;struct select *q;int k;printf("输入物品的数量:");scanf("%d",&n);getchar();p=(struct wp *)malloc(sizeof(struct wp)*n);q=(struct select *)malloc(sizeof(struct select)*n);input(p,n);sort(p,n);k=fun(p,q,n);output(q,k);return 0;
}

吐槽

长,强行用指针、malloc和模块化感觉很乱,因此我们领悟思路,直接不看代码写

思路

我们只需要理解,price/weight==val
如果val越高,就越优先处理即可
那么我们最终只需要一个结构体排序即可

AC代码

#include<iostream>
#include<algorithm>using namespace std;struct Node{string name;int price,weight;
};bool cmp(Node a,Node b){return a.price/a.weight>b.price/b.weight;}//自定义结构体sort降序int main()
{int sum,n;cin>>sum>>n;const int len = n;Node node[len+10];for(int i=0;i<len;i++)cin>>node[i].name>>node[i].weight>>node[i].price;sort(node,node+len,cmp);int cnt=0;while(sum){if(sum>=node[cnt].weight){cout<<node[cnt].name<<" "<<node[cnt].weight<<endl;sum-=node[cnt].weight;}//优先处理最高else{cout<<node[cnt].name<<" "<<sum<<endl;sum=0;}cnt++;}   return 0;
}

自学部分

霍夫曼编码

霍夫曼编码是一种广泛用于数据文件压缩的编码方法,压缩率通常在20%到90%之间,霍夫曼编码算法根据字符出现频率,用不同的0,1串来标识字符,从而达到缩短字符串,达到压缩的目的。
还是拿课程中的算法举例:
假设有一个包含1000字符的文件,每个字符占1byte,一共需要8000bits来存储。
如果这1000个字符只包括6种字符,分别是a,b,c,d,e,f那么我们通过3个bit(最多可以标识8个字符)来表示这6个字符,那么总共需要3000bits就可以表示这个字符串了。
用这种三个bit标识一个字符,编码和解码比较简单,但是没有充分考虑每个字符在文件中出现的频率。而霍夫曼编码是结合字符在文件中的频率来进行对字符编码的,出现字符多的编码更短,由于霍夫曼编码的长短是不一样的,所以如何不让两种不同的编码之间产生混淆?那就需要保证每种编码不能为另一种编码的前缀。
假设这些字符在文件中出现的频率如下:

总bits = 1450 +2350+390+460+530+520 = 2100
2100bits比原来3000bits又压缩了近1/3, 下面问题就是如何进行霍夫曼编码了。
王争老师的算法很巧妙也简单:
1)把所有涉及到的字符按照出现频率的高低放入到优先级队列中区。
2)我们从队列中取出频率最小的两个字符上图中为f和e,然后新建个字符比如X,频率为f和e的频率之和,然后X作为f和e的父亲节点。
3)再把X节点放入到优先级队列中。
4)转到2继续指向,直到队列为空。

构造完一颗二叉树之后,我们给每条边都做个编码,比如左边的边为0,右边的为1,得到如下:

这样每个节点的编码可以用从根节点到此节点的边来表示:
1)比如a这个节点编码为1
2)c这个编码为001。

颜老板版本

#include <iostream>
using namespace std;//最大字符编码数组长度
#define MAXCODELEN 100
//最大哈夫曼节点结构体数组个数
#define MAXHAFF 100
//最大哈夫曼编码结构体数组的个数
#define MAXCODE 100
#define MAXWEIGHT  10000;typedef struct Haffman
{//权重int weight;//字符char ch;//父节点int parent;//左儿子节点int leftChild;//右儿子节点int rightChild;
}HaffmaNode;typedef struct Code
{//字符的哈夫曼编码的存储int code[MAXCODELEN];//从哪个位置开始int start;
}HaffmaCode;HaffmaNode haffman[MAXHAFF];
HaffmaCode code[MAXCODE];void buildHaffman(int all)
{//哈夫曼节点的初始化之前的工作, weight为0,parent,leftChile,rightChile都为-1for (int i = 0; i < all * 2 - 1; ++i){haffman[i].weight = 0;haffman[i].parent = -1;haffman[i].leftChild = -1;haffman[i].rightChild = -1;}std::cout << "请输入需要哈夫曼编码的字符和权重大小" << std::endl;for (int i = 0; i < all; i++){std::cout << "请分别输入第个" << i << "哈夫曼字符和权重" << std::endl;std::cin >> haffman[i].ch;std::cin >> haffman[i].weight;}//每次找出最小的权重的节点,生成新的节点,需要all - 1 次合并int x1, x2, w1, w2;for (int i = 0; i < all - 1; ++i){x1 = x2 = -1;w1 = w2 = MAXWEIGHT;//注意这里每次是all + i次里面便利for (int j = 0; j < all + i; ++j){//得到最小权重的节点if (haffman[j].parent == -1 && haffman[j].weight < w1){//如果每次最小的更新了,那么需要把上次最小的给第二个最小的w2 = w1;x2 = x1;x1 = j;w1 = haffman[j].weight;}//这里用else if而不是if,是因为它们每次只选1个就可以了。else if(haffman[j].parent == -1 && haffman[j].weight < w2){x2 = j;w2 = haffman[j].weight;}}//么次找到最小的两个节点后要记得合并成一个新的节点haffman[all + i].leftChild = x1;haffman[all + i].rightChild = x2;haffman[all + i].weight = w1 + w2;haffman[x1].parent = all + i;haffman[x2].parent = all + i;std::cout << "x1 is" << x1 <<" x1 parent is"<<haffman[x1].parent<< " x2 is" << x2 <<" x2 parent is "<< haffman[x2].parent<< " new Node is " << all + i << "new weight is" << haffman[all + i].weight << std::endl;}
}//打印每个字符的哈夫曼编码
void printCode(int all)
{//保存当前叶子节点的字符编码HaffmaCode hCode;//当前父节点int curParent;//下标和叶子节点的编号int c;//遍历的总次数for (int i = 0; i < all; ++i){hCode.start = all - 1;c = i;curParent = haffman[i].parent;//遍历的条件是父节点不等于-1while (curParent != -1){//我们先拿到父节点,然后判断左节点是否为当前值,如果是取节点0//否则取节点1,这里的c会变动,所以不要用i表示,我们用c保存当前变量iif (haffman[curParent].leftChild == c){hCode.code[hCode.start] = 0;std::cout << "hCode.code[" << hCode.start << "] = 0" << std::endl;}else{hCode.code[hCode.start] = 1;std::cout << "hCode.code[" << hCode.start << "] = 1" << std::endl;}hCode.start--;c = curParent;curParent = haffman[c].parent;}//把当前的叶子节点信息保存到编码结构体里面for (int j = hCode.start + 1; j < all; ++j){code[i].code[j] = hCode.code[j];}code[i].start = hCode.start;}}int main()
{std::cout << "请输入有多少个哈夫曼字符" << std::endl;int all = 0;std::cin >> all;if (all <= 0){std::cout << "您输入的个数有误" << std::endl;return -1;}buildHaffman(all);printCode(all);for (int i = 0; i < all; ++i){std::cout << haffman[i].ch << ": Haffman Code is:";for (int j = code[i].start + 1; j < all; ++j){std::cout << code[i].code[j];}std::cout << std::endl;}return 0;
}

更新版

太长了,应该也不会考,懒得写

第三届蓝桥杯【省赛试题8】密码发生器

在对银行账户等重要权限设置密码的时候,我们常常遇到这样的烦恼:
如果为了好记用生日吧,容易被破解,不安全;如果设置不好记的密码,又担心自己也会忘记;
如果写在纸上,担心纸张被别人发现或弄丢了…
这个程序的任务就是把一串拼音字母转换为6位数字(密码)。
我们可以使用任何好记的拼音串(比如名字,王喜明,就写:wangximing)作为输入,程序输出6位数字。


变换的过程如下:
第一步. 把字符串6个一组折叠起来,
比如 wangximing
则变为:
wangxi
ming
第二步. 把所有垂直在同一个位置的字符的ascii码值相加,得出6个数字,如上面的例子,
则得出:
228 202 220 206 120 105
第三步. 再把每个数字“缩位”处理:
就是把每个位的数字相加,得出的数字如果不是一位数字,就再缩位,直到变成一位数字为止。
例如: 228 => 2+2+8=12 => 1+2=3

上面的数字缩位后变为:344836, 这就是程序最终的输出结果!


要求程序从标准输入接收数据,在标准输出上输出结果。
输入格式为:第一行是一个整数n(<100),表示下边有多少输入行,接下来是n行字符串,就是等待变换的字符串。

输出格式为:n行变换后的6位密码。

例如,
输入:
5
zhangfeng
wangximing
jiujingfazi
woaibeijingtiananmen
haohaoxuexi
则输出:
772243
344836
297332
716652
875843

颜老板版本

#include<stdio.h>
#include<math.h>
#include<string.h>
int f(int x)
{int sum;while(x>=10){sum=0;while(x>0){sum=sum+x%10;x=x/10;}x=sum;}return x;
}int main()
{char a[100];int i,result[100],n,k;scanf("%d",&n);getchar();for(k=0;k<=n-1;k++){int b[6]={0};gets(a);for(i=0;i<=strlen(a)-1;i++){b[i%6]=b[i%6]+a[i];}result[k]=f(b[0])*100000+f(b[1])*10000+f(b[2])*1000+f(b[3])*100+f(b[4])*10+f(b[5])*1;}for(k=0;k<=n-1;k++)printf("%d\n",result[k]);return 0;
}

吐槽

我发现没什么好吐槽的,思路非常重要,换谁保险也都得这么做
需要注意的几个点

for(i=0;i<=strlen(a)-1;i++){b[i%6]=b[i%6]+a[i];}

可以帮助字符串直接对位记录

while(x>=10){sum=0;while(x>0){sum=sum+x%10;x=x/10;}x=sum;}

取出各个位次相加,然后检查是否又>10

result[k]=f(b[0])*100000+f(b[1])*10000+f(b[2])*1000+f(b[3])*100+f(b[4])*10+f(b[5])*1;

本来我觉得这个写法很low,试了一波之后感觉真他妈香气扑鼻

鸡蛋里找骨头

for(k=0;k<=n-1;k++)printf("%d\n",result[k]);

可以直接输出而不储存数组

蛋疼环节

memset(tmparr,0,sizeof tmparr);
因为长度的问题,我没有清零时似乎出现已赋值的问题

思路

1.分列存储
2.按位相加
3.更新输出

AC代码

#include<iostream>
#include<cstring>using namespace std;const int len =6;int check(int x)
{int sum;while(x>=10){sum=0;while(x){sum=sum+x%10;x=x/10;}x=sum;}return x;
}int main()
{int n;cin>>n;int tmparr[len+10];for(int i=0;i<n;i++){memset(tmparr,0,sizeof tmparr);string s;cin>>s;for(int j=0;j<s.size();j++)tmparr[j%6]=tmparr[j%6]+s[j];    cout<<check(tmparr[0])*100000+check(tmparr[1])*10000+check(tmparr[2])*1000+check(tmparr[3])*100+check(tmparr[4])*10+check(tmparr[5])*1<<endl;}return 0;
}

[算法课]全面翻新计划!第十二周全解相关推荐

  1. 十二步解N-S方程之第四步

    2019独角兽企业重金招聘Python工程师标准>>> 十二步解N-S方程之第四步 通过前面的学习,我们已经知道如何去解线性和非线性的一维对流方程,而且也知道CFL是如何影响数值求解 ...

  2. 静态树表查找算法及C语言实现,数据结构算法C语言实现(三十二)--- 9.1静态查找表...

    一.简述 静态查找表又分为顺序表.有序表.静态树表和索引表.以下只是算法的简单实现及测试,不涉及性能分析. 二.头文件 /** author:zhaoyu date:2016-7-12 */ #inc ...

  3. 【机器学习算法专题(蓄力计划)】二十、实操代码MNIST 数据集

    文章目录 MNIST 数据集 sklearn 加载的数据集通常具有类似字典结构 训练一个二元分类器 性能考核 混淆矩阵 精度 / 召回率权衡 ROC 曲线 多类别分类器 solution one so ...

  4. 撩课-Java面试宝典-第十二篇

    学习地址: 撩课-JavaWeb系列1之基础语法-前端基础 撩课-JavaWeb系列2之XML 撩课-JavaWeb系列3之MySQL 撩课-JavaWeb系列4之JDBC 撩课-JavaWeb系列5 ...

  5. 【机器学习算法专题(蓄力计划)】二、机器学习中的统计学习方法概论

    一. 统计学习方法概论 统计学习的定义.研究对象和方法 监督学习.无监督学习.半监督学习.强化学习 统计学习方法三要素:模型.策略.算法 模型选择:正则化.交叉验证.学习的泛化能力 生成模型和判别模型 ...

  6. 渡一教育公开课web前端开发JavaScript精英课学习笔记(三十二)JavaScript旋转方块

    JavaScript旋转方块 <!DOCTYPE html> <html lang="en"><head><meta charset=&q ...

  7. 《老喻的人生算法课》之A计划笔记(二)

    本文发于微信公众帐号: 一界码农(The_hard_the_luckier) 无需授权即可转载: 甚至无需保留以上版权声明- 接着上篇来加深对人生算法A计划的九段位理解. 初段-闭环:如何对抗完美主义 ...

  8. 八十二、Python | Leetcode贪心算法系列

    @Author:Runsen @Date:2020/7/5 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰 ...

  9. 十二之续、快速排序算法的深入分析

    十二之续.快速排序算法的深入分析 作者:July   二零一一年二月二十七日 -------------------------- 前言 一.快速排序最初的版本 二.Hoare版本的具体分析 三.Ho ...

最新文章

  1. 黑龙江科技大学计算机是几本,黑龙江科技大学是几本
  2. 位操作(Bit manipulation)
  3. 第三部分:Android 应用程序接口指南---第二节:UI---第八章 Toast通知
  4. 为@Cacheable设置TTL – Spring
  5. Linux守护进程的创建(结合nginx框架)
  6. 昨天7月21号,笑笑又生病了
  7. mysql execution plan_MySQL Execution Plan--NOT IN查询
  8. LeetCode962. 最大宽度坡
  9. Android网页字体缩放控件,Zoom Text Only插件下载-chrome网页字体缩放插件Zoom Text Only下载v1.5.4 官方版-西西软件下载...
  10. Linux文件系统(一)——常用文件系统
  11. 上海宝付高谈保险扣费如何追回
  12. 在图片上添加文字并生成图片
  13. 第13章-1~3 法兰接头预紧力密封接触分析(模型简化、网格调整、约束)并行计算设置、摩擦接触设置adjust to touch 、约束设置frictionless support、bolt
  14. 刚刚整理好-汉字转拼音缩写的函数(C#)
  15. 彻底吃透监控系统,就这一篇!
  16. termux安装以及基本配置
  17. 2023/1/2总结
  18. Robomongo与MongoDB的故事
  19. win10 如何开启guest
  20. eeglab的安装和安装时遇到的问题

热门文章

  1. c语言中入口参数是什么,C语言中入口参数是什么
  2. C语言单目运算符和双目运算符、三目运算符
  3. 相控阵基础之1-波束倾斜(Beam Squint)
  4. python的水藻生长问题_如何抑制水中藻类生长
  5. 微信支付重复调用解决方案
  6. 入耳式蓝牙耳机哪个牌子好用?四款高品质音乐游戏耳机
  7. Android开发——使用Jword生成本地word文档
  8. 2018 计蒜之道-初赛 第二场 B-阿里巴巴的手机代理商(简单)
  9. MNL——多项Logit模型学习笔记(一)离散选择以及logit模型介绍
  10. Linux服务器上使用nginx搭建mp4、flv流媒体服务器