十三周算法训练
//1、Fibonacci数列(至少写出3种算法)
//2、π值求法(至少写出2种算法)
3、循环赛日程表(至少写出3种算法)
//4、0-1背包问题(至少写出3种算法)
//5、最大子段和问题(至少写出3种算法)
//6、判断一个字符串是不是IP地址
要求:1写出算法思想2编程语言为C语言2时间复杂度和空间复杂度4比较各个算法,找出最优算法和算法优化的思路。
(注:从本周开始引入各种算法思想如:分治递归、动态规划、贪心、回溯、分支限界等。)
一、Fibonacci数列
问题描述
1202年,意大利数学家 Fibonacci出版了《算盘全书》。他在书中提出了一个关于兔子繁殖的问题:如果一对兔子(一雄一雌)每月能生一对小兔子,而每对小兔从出生后的第三个月开始,每个月又能生一对小兔,假定在不发生死亡的情況下,由一对出生的小兔开始,问一年后会有多少对兔子?
二、π值求法
问题描述
π是一个在数学及物理学领域普遍存在的数学常数。大写Ⅱ,小写π。π表示数学圆周率,它定义为平面上圆的周长与直径之比,也等于圆的面积与半径平方的比值。
中国数学家刘徽在注释《九章算术》(263年)时用圆的内接正多边形求得π的近似值,也得出精确到两位小数的π值,他的方法被后人称为割圆术。
电子计算机的出现使π值的计算有了突飞猛进的发展。1949年美国马里兰州阿伯丁的军队弹道研究实验室首次用计算机(ENIAC)计算π值,一下子就算到2037位小数突破了小数点后千位数字。1989年美国哥伦比亚大学研究人员用克雷-2型和 ibm-ve 型巨型电子计算机计算出π值小数点后4.8亿位数,后又继续算到小数点后10.1亿位数,创下新的纪录。至今,最新纪录是小数点后25769亿位数。
三、循环赛日程表
问题描述
所谓循环赛,是指每个队都能和其他队比赛一次,最后按成绩计算名次。这种竞赛方法比较合理、客观和公平,有利于各队相互学习和交流经验。假设有n位选手参加循环赛,循环赛共进行n-1天,每位选手要与其他n-1位选手比赛一场,且每位选手每天必须比赛一场,不能轮空,要求为比赛安排日程。
四、背包问题
问题描述
背包问题(Knapsack problem)是一种组合优化的NP完全问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中。相似问题经常出现在商业、组合数学,计算复杂性理论、密码学和应用数学等领域中。也可以将背包问题描述为决定性问题,即在总重量不超过W的前提下,总价值是否能达到V?它是在1978年由Merkel和Hellman提出的。
它的主要思路是假定某人拥有大量物品,重量各不同。此人通过秘密地选择一部分物品并将它们放到背包中来加密消息。背包中的物品总重量是公开的,所有可能的物品也是公开的,但背包中的物品是保密的。附加一定的限制条件,给出重量,而要列出可能的物品,在计算上是不可实现的。背包问题是熟知的不可计算问题,背包体制以其加密解密速度快而引人注目。
设有一个背包可以放入的物品重量为s,现有n件物品,重量分别是w1,w2,w3,…,wn。问能否从这n件物品中选择若干件放入背包中,使得放入的重量之和正好为s。如果有满足条件的选择,则此背包有解,否则此背包问题无解。
五、最大子段和问题
问题描述
最大子段和是一个常见而且经典的模型,又是一类基本的DP模型,最大子段问题是对于给定序列[x1,x2,x3,…]寻找它的某个连续子段,使得其和最大。如:{-1,5,-2 1,-7,-4,2,3,-1,2}的最大子段是{2,3,-1,2},其和为6。这个问题可以通过枚举分治、动态规划等几种方法来求解。
六、判断一个字符串是不是IP地址
注:十四周先分组讲述分治递归、动态规划、贪心这三个算法思想。
// 一 数列
//1
#include<stdio.h> //F[n]=F[n-1]+F[n-2](n>=2,F[0]=0,F[1]=1 n=2 1)
long long a[10000];
long long f(int x)
{ if(x==0) return a[x]=0;
if(x==1||x==2) return a[x]=1;
else if (a[x]>-1) return a[x];
else return a[x]=f(x-1)+f(x-2);
}
int main()
{
int n;,
int i;
while(1)
{
for(i=0;i<10000;i++) //初始化 -1标识
{
a[i]=-1;
}
printf("in put n:");
scanf("%d",&n);
printf("\n%lld\n",f(n));
}
return 0;
}
//o(2^n)
//2
#include<stdio.h>
long long a[10000];
long long f(int x)
{
if(x==0)
return 0;
if(x==1||x==2)
return 1;
else
return f(x-1)+f(x-2);
}
int main()
{
int n,i;
while(1)
{
printf("in put n:");
scanf("%d",&n);
printf("\n%lld\n",f(n));
}
return 0;
}
//o(2^n-1)
//3
#include<stdio.h>
int main()
{
long long number,t1,t2;
int n,i;
while(1)
{
printf("in put n:");
scanf("%d",&n);
if(n==1)
{
printf("1\n");
}
else if(n==2)
{
printf("1\n");
}
else if(n>2)
{
t1=1;t2=1;
for(i=0;i<n-2;i++)
{
number=t1+t2;
t2=t1;
t1=number;
}
printf("\n%lld\n",number);
}
else
printf("error!");
}
return 0;
}
//o(n)
//=====0 1 背包 ====================================================================
//1 动态
#include<stdio.h>
int V[1000][1000];
int c,n,w[1000],v[1000];// x选取标识 c容量 n物品个数 w物品重量 v价值
int max(int a,int b)
{
if(a>=b)
return a;
else return b;
}
int main()
{
int i,j,s; //s最大价值
printf("输入 容量:");
scanf("%d",&c);
printf("输入 个数:");
scanf("%d",&n);
printf("输入物品重量:");
for(i=0;i<n;i++)
{
scanf("%d",&w[i]);
}
printf("输入物品价值:");
for(i=0;i<n;i++)
{
scanf("%d",&v[i]);
}
for(i=1;i<=n;i++) // 列 控制列数
{
for(j=1;j<=c;j++) //行 控制行数
{
if(j<w[i-1])
{
V[i][j]=V[i-1][j]; // V 用来记录最优价值
}
else
{
V[i][j]=max(V[i-1][j],V[i-1][j-w[i-1]]+v[i-1]); //状态转移方程 判断这个物品放还是不放
}
}
}
j=c;
for(i=n;i>=1;i--)
{
if(V[i][j]>V[i-1][j])
{
x[i]=1;
j=j-w[i-1];
}
else
x[i]=0;
}
printf("选中的物品是:\n");
for(i=1;i<=n;i++)
printf("%d ",x[i]);
printf("\n");
printf("%d\n",V[n][c]);
}
//o(n^2)
//2 贪心算法
#include<stdio.h>
typedef struct aa
{
double x; //性价比
int z; //记录位置
int w; //重量
int v; // 价值
}aa;
int main()
{
aa y[1000],temp;
int flag[1000],cc,vv;
int i,j,c,n;
printf("输入 容量:");
scanf("%d",&c);
printf("输入 个数:");
scanf("%d",&n);
printf("输入物品重量:");
for(i=0;i<n;i++)
{
scanf("%d",&y[i].w);
}
printf("输入物品价值:");
for(i=0;i<n;i++)
{
scanf("%d",&y[i].v);
}
for(i=0;i<n;i++) //计算性价比
{
y[i].x=(y[i].v*1.0)/(y[i].w*1.0);
y[i].z=i;
}
for(i=0;i<n-1;i++) //冒泡 小到大 n个数进行n-1次排序 控制次数
{
for(j=0;j<n-1-i;j++) //前后冒泡开始比较 控制位次 n-1-i之后的已经排好顺序
{
if(y[j].x>y[j+1].x)
{
temp=y[j];
y[j]=y[j+1];
y[j+1]=temp;
}
}
}
cc=0;
vv=0;
for(i=0;i<n;i++)
{
flag[i]=-1;
}
for(i=n-1;i>=0;i--) //从上倒下 依次放入
{
if(y[i].w>c) // 判断能否装下 能装就装 否者跳过
{
continue;
}
if((cc+y[i].w)<c)
{
cc=cc+y[i].w;
flag[i]=y[i].z;
vv=y[i].v+vv;
}
else
break;
}
printf("最大价值:%d \n",vv);
for(i=0;i<n;i++)
{
if(flag[i]!=-1)
{
printf("选取的位置:%d ",flag[i]+1);
}
}
printf("\n");
return 0;
}
//o(n^2)
//3 暴力大法
// 把n 换成000000 到 11111111 暴力穷举所有可能
#include<stdio.h>
int cheng(int n)
{
int i,j=1;
for(i=0;i<n;i++)
{
j=j*2;
}
return j;
}
int main()
{
int n,c,w[1000],v[1000];
int i,j,z,ww,vv,max,rember;
printf("输入 容量:");
scanf("%d",&c);
printf("输入 个数:");
scanf("%d",&n);
printf("输入物品重量:");
for(i=0;i<n;i++)
{
scanf("%d",&w[i]);
}
printf("输入物品价值:");
for(i=0;i<n;i++)
{
scanf("%d",&v[i]);
}
max=0;
for(i=0;i<cheng(n);i++)
{
z=i;
ww=0;
vv=0;
for(j=0;j<n;j++) //2进制转换 先% 再/ 结果倒排为2进制数
{
if(z%2==1)
{
ww=ww+w[j];
vv=vv+v[j];
}
z=z/2;
}
if((ww<=c)&&(vv>max))
{
max=vv;
rember=i;
}
}
printf("选取位置:");
for(i=0;i<n;i++)
{
if(rember%2==1)
{
printf("%d ",i+1);
}
rember=rember/2;
}
printf("最大价值:%d\n",max);
return 0;
}
//o(2^n*n)
//===π=============================================================================
//1
#include <stdio.h>// π/4 =1-1/3+1/5-1/7+1/9.......
#include <stdlib.h>
#include <math.h>
int main(){
float s=1;
double pi=0;
float i=1.0;
float n=1.0;
while(fabs(i)>=1e-7){
pi+=i;
n=n+2;
s=-s;
i=s/n;
}
pi=4*pi;
printf("pi的值为:%.10f\n",pi);
return 0;
}
//2
#include <stdio.h> //pai/2= 2/1*2/3*4/3*4/5*6/5*6/7
#include <math.h> //分子 第n个时 n为偶数 =n n为奇数 =n+1
int main(){ // 分母 第n个时 n为偶数 =n+1 n为奇数=n
double pi=1.0;
float n=1.0; //
int j;
for(j=1;j<=10000000;j++)
{
if(j%2==0)
{
pi=pi*(n/(n+1));
}else
{
pi=pi*((n+1)/n);
}
n++;
}
pi=2*pi;
printf("pi的值为:%.6lf\n",pi);
return 0;
}
//=====子段=====================================================================
//(-2,11,-4,13,-5,2)的最大子段和为20,所求子区间为[2,4].
//暴力大法
#include<stdio.h>
int main()
{
int start=0,end=0,max;
int i,j,n,sum=0;
int num[1000];
printf("输入有多少个数值:");
scanf("%d",&n);
printf("输入数值:");
for(i=0;i<n;i++)
{
scanf("%d",&num[i]);
}
max=num[0];
for(i=0;i<n;i++) //控制起始位置
{
sum=0;
for(j=i;j<n;j++) //遍历结束位置
{
sum=sum+num[j];
// printf("%d ",sum);
if(sum>max)
{
start=i+1; //记录位置
end=j+1;
max=sum;
}
}
}
printf("最大和为:%d \n区间为:%d--%d",max,start,end);
return 0;
}
o(n^2)
// 分治
#include<stdio.h>
int max(int i, int j, int k) //求3者最大值 依此比较 i,j,k
{
if (i>=j && i>=k)
return i;
return max(j, k, i);
}
int max2(int a[], int r, int l)
{
int lmax,lsum,rmax,rsum,m,i;
if (r < l)
{
return 0;
}
if (r == l)
{
return a[l];
}
m = (r + l) / 2; //计算 中间位置
lmax=a[m], lsum=0;
for (i=m; i>=l; i--) //计算 左侧 从右边开始连续的最大值
{
lsum =lsum+a[i];
if (lsum > lmax)
{
lmax = lsum;
}
}
rmax=a[m+1], rsum = 0; //计算 右侧的 从左边开始的连续最大值
for (i=m+1; i<=r; i++)
{
rsum=rsum+a[i];
if (rsum > rmax)
{
rmax = rsum;
}
}
return max(lmax+rmax, max2(a, l, r), max2(a, l+1, r)); //返回三者最大值
}
int main()
{
int i,n;
int num[1000];
printf("输入有多少个数值:");
scanf("%d",&n);
printf("输入数值:");
for(i=0;i<n;i++)
{
scanf("%d",&num[i]);
}
printf("%d",max2(num,n-1,0));
return 0;
}
//o(N*lg^N)
//3 递推求和
#include<stdio.h>
int max3(int *a, int n)
{
int max, flag,i;
max = flag = a[0]; //max记录最大值, flag记录相加大于0的数值和
for (i=1;i<n;i++) //因为对 max flag 用a[0]进行了初始化 则i从1开始 进行n-1次 遍历
{
if (flag <= 0) //先对flag进行处理,只保存对相加有利的和 也就是>1的
{
flag = a[i]; //否则 舍弃 从当前位开始重新相加
}
else
{
flag=flag+a[i];
}
if (flag > max) //max记录flag出现过的最大值
{
max=flag;
}
}
return max;
}
int main()
{
int i,n;
int num[1000];
printf("输入有多少个数值:");
scanf("%d",&n);
printf("输入数值:");
for(i=0;i<n;i++)
{
scanf("%d",&num[i]);
}
printf("%d",max3(num,n));
return 0;
}
//o(n)
/// =====================ip地址========================================
#include<stdio.h> //区分 . 判断10进制 还是32进制
#include<string.h>
int judge1(char *ip) //先区分 01 还是普通
{
int i,count;
for(i=0;i<33;i++)
{
if(ip[i]=='.')
{
return 1;
}
}
return 0;
}
int judge2(char *ip,int n) //普通
{
int i,count=0,number=0,flag=0,j=0;
char a[3],b[3],c[3],d[3];
for(i=0;i<n;i++)
{
if(((ip[i]<'0')||(ip[i]>'9'))&&(ip[i]!='.')) //判断是否是数字
{
return 0;
}
if(ip[i]=='.') //遇到 . 后 判断是否大于 255
{
count++;
if(number>=0&&number<=255)
{
number=0;
}
else
return 3;
}
if(ip[i]!='.')
{
number=number*10+(ip[i]-'0'); //转换成数值
}
}
if(count!=3) //判断 . 的个数
{
return 4;
}
for(i=0;i<3;i++)
{
a[i]=b[i]=c[i]=d[i]='!';
}
flag=0;
j=0;
for(i=0;i<n;i++)
{
if(ip[i]=='.')
{
i++;
break;
}
a[j]=ip[i];
j++;
}
j=0;
for(i;i<n;i++)
{
if(ip[i]=='.')
{
i++;
break;
}
b[j]=ip[i];
j++;
}
j=0;
for(i;i<n;i++)
{
if(ip[i]=='.')
{
i++;
break;
}
c[j]=ip[i];
j++;
}
j=0;
for(i;i<n;i++)
{
if(ip[i]=='.')
{
i++;
break;
}
d[j]=ip[i];
j++;
}
for(i=0;i<1;i++) //除去 012 的可能
{
if(a[i]=='0'&&a[i+1]>'0')
{
return 9;
}
if(b[i]=='0'&&b[i+1]>'0')
{
return 9;
}
if(c[i]=='0'&&c[i+1]>'0')
{
return 9;
}
if(d[i]=='0'&&d[i+1]>'0')
{
return 9;
}
}
/* for(i=0;i<40;i++)
{
a[i]=-1;
}
for(i=1;i<n+1;i++)
{
a[i]=ip[i-1];
}
for(i=0;i<n+2;i++)
{
if((a[i]==-1)&&a[i+1])
}*/
return 1;
}
int judge3(char *ip) //01
{
}
int main()
{
char ip[33];
int i,j,pan,pan1,len=0;
printf("输入 IP 地址:");
gets(ip);
len=strlen(ip);
pan=judge1(ip);
printf("%d ",pan);
if(pan==1)//普通
{
pan1=judge2(ip,len);
}
if(pan==0)//01
{
pan1=judge3(ip);
}
printf("%d ",pan1);
if(pan1==1)
{
printf("ok");
}
else
printf("no!");
return 0;
}
///
#include<stdio.h>
int a[1000][1000];
int put_out(int n,int k) //n个选手 k是阶次
{
int i,j,l,m,p;
for(i=1;i<=n;i++)
{
a[1][i]=i;
}
/* for(i=0;i<n;i++)
{
printf("%d ",a[0][i]);
}
*/
m=1;
for(l=0;l<k;l++) //控制填充次数
{
n=n/2;
for(p=1;p<=n;p++) //左右填充次数 n次最小块的调换
{
//控制的对角线调换
for(i=m+1;i<=2*m;i++) //控制最小块行
{
for(j=m+1;j<=2*m;j++) //最小块的列
{
a[i][j+(p-1)*m*2]=a[i-m][j+(p-1)*m*2-m];
a[i][j+(p-1)*m*2-m]=a[i-m][j+(p-1)*m*2];
}
}
}
m=m*2;
}
return 1;
}
int main()
{
int k,i,n=1,j,count=1;
printf("输入2^k的k值:");
scanf("%d",&k);
for(i=0;i<k;i++)
{
n=2*n;
}
// printf("%d\n",n);
put_out(n,k);
for(i=1;i<=n;i++)
{
printf("%d号 :",count++);
for(j=2;j<=n;j++)
{
printf("%d ",a[i][j]);
}
printf("\n");
}
return 0;
}