视频地址:http://list.youku.com/albumlist/show/id_52122718
视频1.4地址:https://www.bilibili.com/video/av45870006/
1.1 编程的灵魂:数据结构+算法
1.2 算法的作用:猜价格游戏
1.3 递推算法
1.4 枚举(穷举)算法
1.5 递归算法
举例:求阶乘、进制转换
1.6 分治算法
举例:乒乓球比赛赛程安排
分治算法主要思想:
(1)若原问题规模大小不便计算,将问题原本规模分解成若干个规模较小的相同问题;
(2)每个子问题求解相互独立,且与原问题形式相同,递归解这些子问题合并后组成原问题答案;
分治算法适用问题:
(1)问题可分解成若干个规模较小的相同问题;
(2)合并子问题的解可以得到原问题的解;
(3)各个子问题相互独立;
分治算法的主要步骤:
(1)分解:缩小问题规模;
(2)求解:足够小的规模可用简单方法求解;
(3)合并:按照求解问题要求,合并子问题的解,构成最终的解;
#include<stdio.h>
#define MAXN 64
int a[MAXN+1][MAXN+1]={0};
void gamecal(int k,int n) //处理编号k开始的n个选手的日程
{
int i,j;
if(n==2) //递归出口
{
a[k][1]=k; //参赛选手编号
a[k][2]=k+1; //对阵选手编号
a[k+1][1]=k+1; //参赛选手编号
a[k+1][2]=k; //对阵选手编号
}else{
gamecal(k,n/2); //递归调用
gamecal(k+n/2,n/2);
for(i=k;i<k+n/2;i++) //填充右上角
{
for(j=n/2+1;j<=n;j++)
{
a[i][j]=a[i+n/2][j-n/2];
}
}
for(i=k+n/2;i<k+n;i++) //填充右下角
{
for(j=n/2+1;j<=n;j++)
{
a[i][j]=a[i-n/2][j-n/2];
}
}
}
}
int main()
{
int m,i,j;
printf("输入参赛选手的人数:");
scanf("%d",&m);
j=2;
for(i=2;i<8;i++)
{
j=j+2;
if(j==m) break;
}
if(i>=8)
{
printf("参赛选手人数必须为2的整数次幂,且不超过64!\n");
getchar();
return 0;
}
gamecal(1,m);
printf("\n编号");
for(i=2;i<=m;i++)
printf("%2d天",i-1);
printf("\n");
for(i=1;i<=m;i++)
{
for(j=1;j<=m;j++)
printf("%4d",a[i][j]);
printf("\n");
}
getchar();
return 0;
}
1.7 贪婪算法
贪心算法的基本思路:从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。当达到算法中的某一步不能再继续前进时,算法停止。该算法存在问题:1. 不能保证求得的最后解是最佳的;2. 不能用来求最大或最小解问题;3. 只能求满足某些约束条件的可行解的范围。
实例:找零钱
#include<stdio.h>
#define MAXN 9 //表示9种面额的人民币
int parvalue[MAXN]={10000,5000,1000,500,200,100,50,20,10}; //为了使用整数计算,以分为单位
int num[MAXN]={0}; //保存对应面额人民币的张数
int exchange(int n) //n表示找零数
{
int i;
for(i=0;i<MAXN;i++)
if(n>parvalue[i]) break; //找到比n小的最大面额
while(n>0 && i<MAXN)
{
if(n>=parvalue[i])
{
n=n-parvalue[i];
num[i]++;
}else if(n<10 && n>=5)
{
num[MAXN-1]++;
break;
}else
i++;
}
return 0;
}
int main(){
int i;
float m;
printf("请输入找零的金额:");
scanf("%f",&m);
exchange(int(100*m));
printf("\n%.2f元零钱的组成:\n",m);
for(i=0;i<MAXN;i++)
if(num[i]>0)
printf("%6.2f: %d张\n",(float)parvalue[i]/100.0,num[i]);
getchar();
return 0;
}
1.8 试探性算法(回溯法)
算法思路:为了求得问题的解,先选择某一种可能的情况进行试探,在试探过程中,一旦发现此选择的假设是错误的,就退回一步重新选择,继续向前试探,如此反复进行,直到证明解或者无解。例如你下棋的时候,自己先思考某一棋路,但是考虑到自己下这一步后对手下了对自己不利,所以换一种下法,类似于此就称为试探法。
举例:生成彩票号码组合、八皇后问题
//本程序代码不简洁,繁琐
//程序没有通用性,彩票代码的位数必须固定
//解决办法:采用试探法
#include<stdio.h>
int main(int n) //n表示找零数
{
int i[7],j;
for(i[0]=1;i[0]<=29;i[0]++)
for(i[1]=1;i[1]<=29;i[1]++)
{
if(i[0]==i[1]) continue;
for(i[2]=1;i[2]<=29;i[2]++)
{
if(i[0]==i[2] || i[1]==i[2]) continue;
for(i[3]=1;i[3]<=29;i[3]++)
{
if(i[0]==i[3] || i[1]==i[3] ||
i[2]==i[3]) continue;
for(i[4]=1;i[4]<=29;i[4]++)
{
if(i[0]==i[4] || i[1]==i[4] ||
i[2]==i[4] || i[3]==i[4]) continue;
for(i[5]=1;i[5]<=29;i[5]++)
{
if(i[0]==i[5] || i[1]==i[5] ||
i[2]==i[5] || i[3]==i[5] ||
i[4]==i[5]) continue;
for(i[6]=1;i[6]<=29;i[6]++)
{
if(i[0]==i[6] || i[1]==i[6] ||
i[2]==i[6] || i[3]==i[6] ||
i[4]==i[6] || i[5]==i[6]) continue;
for(j=0;j<7;j++)
printf("%3d",i[j]);
printf("\n");
getchar();
}
}
}
}
}
}
return 0;
}
//采用回溯法(试探法)
#include<stdio.h>
#define MAXN 7 //设置每一注彩票的位数
#define NUM 29 //设置组成彩票的数字
int num[NUM];
int lottery[MAXN];
void combine(int n,int m) //n为促组成彩票的数字位数,m为每注彩票的位数
{
int i,j;
for(i=n;i>=m;i--)
{
lottery[m-1]=num[i-1]; //保存一位数字
if(m>1)
combine(i-1,m-1); //递归调用
else //若m=1,输出一注号码
{
for(j=MAXN-1;j>=0;j--)
printf("%3d",lottery[j]);
getchar();
printf("\n");
}
}
}
int main(){
int i;
for(i=0;i<NUM;i++) //设置彩票各位数字
num[i]=i+1;
for(i=0;i<MAXN;i++)
lottery[i]=0;
combine(NUM,MAXN);
getchar();
return 0;
}
1.9 模拟算法
在程序设计语言中,可以使用随机函数模拟自然界中发生的不可预知的情况。C语言中使用srand()函数和rand()函数可生成随机数。引入上面两个函数要包含头文件#include<time.h>和#include<stdlib.h>
实例:猜数游戏
实例:模拟掷骰子游戏
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
void play(int n)
{
int i,m=0,t=0;
for(i=0;i<n;i++)
{
t=rand()%6+1;
m+=t;
printf("\t第%d粒:%d;\n",i+1,t);
}
printf("\t总点数为:%d\n",m);
}
int main(){
int c; //参赛人数
int n; //骰子数量
int i;
do{
srand(time(NULL));
printf("设置骰子数量(输入0退出):");
scanf("%d",&n);
if(n==0) break; //至少一个筛子
printf("\n输入本轮参赛人人数(输入0退出):");
scanf("%d",&c);
if(c==0) break;
for(i=0;i<c;i++)
{
printf("\n第%d位选手掷出的骰子为:\n",i+1);
play(n);
}
printf("\n");
}while(1);
return 0;
}
1.10 算法的评价
算法的评价原则:正确性、高效性、空间性、可读性
算法的效率:通常认为统计算法中基本操作执行次数就可以近似得到算法的执行效率。称为时间复杂度。