在学习c语言时,总感觉自己学习的不过是皮毛的东西,近几日受大神的建议之下,开始学习算法。同时加以NOIP的练习,虽说我的学习是从最简单的学起的。甚至有些早已在课程中学过,但是若是看实例分析,仍是有些吃力。为了此次的学习,特地学习了基础的c++语法知识。现将我的归纳总结列出如下:(一来是日后给自己一个复习的空间,二来望各位大神指导提出我的错误理解)
一.递归
/*本例子是用来说明基础算法之递归
* 题目:汉诺塔,输入一个整数n表示有n个盘片在第一根柱子上,输出操作序列,使得用最少的操作把所有的盘子* 从a柱子转移到c柱子上
* 解题思路:当盘子的个数为n时,移动的次数为2^n-1。首先把3根柱子按顺序排成“品”字形,把所有的圆盘按从* 大到小的顺序放在柱子A上。若n为偶数,按顺时针依次摆放a,b,c;若为奇数,则按顺时针依次摆放a,c,b
* 1.按顺时针把圆盘1从现在的柱子移动到下一根柱子(即若为偶数,而圆盘1在柱子A上,则把它移动到B上
* 2.接着把另外两根非空柱子上的圆盘移动到新的柱子上;若两根柱子都非空时,移动较小的圆盘
* 3.反复进行1,2操作 */
#include <fstream>
#include <iostream>
using namespace std;
void Move(int n,char x,char y){
cout<<"move "<<n<<" from "<<x<<" to "<<y<<endl;
}
void Hannoi(int n,char a,char b,char c){
if(n==1)
Move(1,a,c);
else{
Hannoi(n-1,a,c,b);
Move(n,a,c);
Hannoi(n-1,b,a,c);
}
}
int main(){
int n;
scanf("%d",&n);
Hannoi(n,'a','b','c');
return 0;
}
/*n=3时,
Hannoi(2,a,c,b);
【Hannoi(1,a,b,c){Move(1,a,c)};Move(2,a,b);Hannoi(1,c,a,b);{Move(1,c,b)}】
Move(3,a,c);
Hannoi(2,b,a,c);
【Hannoi(1,b,c,a){Move(1,b,a)};Move(2,b,c);Hannoi(1,a,b,c);{Move(1,a,c)}】
输出为:move 1 from a to c
move 2 from a to b
move 1 from c to b
move 3 from a to c
move 1 from b to a
move 2 from b to c
move 1 from a to c */
二.分治
/*本例子是用来说明基础算法之分治(基本思路是将一个规模为N的问题分解为K个规**模较小的子问题,这些子问题相互独立且原问题性质相同。
* 题目:计数问题:给定两个数a,b,计算出1在a,b出现的次数,输入两个0时程序结* 束,两个0不作为输入样例
* 解题思路:1.求出190~197之间出现的次数,然后对于0~189.190~197中1在个位* 数上只出现了一次
* 2.个位考虑完后,直接考虑197/10-1(即18)中1出现的次数,同时 * 考虑到,数字减小了,每一位的权值会增加。也就是说每一个数字出现的次数会增 * 加10倍。 */
#include <iostream>
using namespace std;
const int N=11;
int d[N];//d[N]中存储数字0~9分别出现的次数
int value;
void deal(int n);
int main(){
int a,b;
while(cin>>a>>b){
if(a==0&&b==0)break;
if(a<b){int tmp=b;b=a;a=tmp;}//将较大的值存入a,较小值存入b
for(int i=0;i<10;i++)
d[i]=0;//初始化操作
/*处理过程*/
value=1;
deal(a);
value=-1;//此处value=-1是为了求出最后的答案deal(a)-deal(b)
deal(b-1);
/*输出结果*/
cout<<d[1]<<endl;
}
return 0;
}
void deal(int n){
if(n<0)return;
int one,ten;//分别表示个位,十位
one=n%10;
n/=10;
ten=n;
for(int i=0;i<=one;i++)
d[i]+=value;
//1.算出190^197个位出现1的情况;2.算出110-119十位出现1的情况
while(ten){
d[ten%10]+=(one+1)*value;
//1.算出190~197,十位与百位出现1的情况2.算出100-180百(千)位出现1的情况
ten/=10;
}
for(int i=0;i<10;i++)
d[i]+=value*n;
d[0]-=value;//将第一位是0的情况排除
//1.算出1,11,21……,181个位出现1的情况;2.算出10,110十位出现1的情况
value*=10;//权值变化,变为原来的10倍
deal(n-1);//算出18时的情况,即0~189情况,此时权值增加到十倍
}
/*本例子是用来说明基础算法之分治(基本思路是将一个规模为N的问题分解为K个规**模较小的子问题,这些子问题相互独立且原问题性质相同。
* 题目:计数问题:给定两个数a,b,计算出1在a,b出现的次数,输入两个0时程序结* 束,两个0不作为输入样例
* 解题思路:1.求出190~197之间出现的次数,然后对于0~189.190~197中1在个位* 数上只出现了一次
* 2.个位考虑完后,直接考虑197/10-1(即18)中1出现的次数,同时 * 考虑到,数字减小了,每一位的权值会增加。也就是说每一个数字出现的次数会增 * 加10倍。 */
#include <iostream>
using namespace std;
const int N=11;
int d[N];//d[N]中存储数字0~9分别出现的次数
int value;
void deal(int n);
int main(){
int a,b;
while(cin>>a>>b){
if(a==0&&b==0)break;
if(a<b){int tmp=b;b=a;a=tmp;}//将较大的值存入a,较小值存入b
for(int i=0;i<10;i++)
d[i]=0;//初始化操作
/*处理过程*/
value=1;
deal(a);
value=-1;//此处value=-1是为了求出最后的答案deal(a)-deal(b)
deal(b-1);
/*输出结果*/
cout<<d[1]<<endl;
}
return 0;
}
void deal(int n){
if(n<0)return;
int one,ten;//分别表示个位,十位
one=n%10;
n/=10;
ten=n;
for(int i=0;i<=one;i++)
d[i]+=value;
//1.算出190^197个位出现1的情况;2.算出110-119十位出现1的情况
while(ten){
d[ten%10]+=(one+1)*value;
//1.算出190~197,十位与百位出现1的情况2.算出100-180百(千)位出现1的情况
ten/=10;
}
for(int i=0;i<10;i++)
d[i]+=value*n;
d[0]-=value;//将第一位是0的情况排除
//1.算出1,11,21……,181个位出现1的情况;2.算出10,110十位出现1的情况
value*=10;//权值变化,变为原来的10倍
deal(n-1);//算出18时的情况,即0~189情况,此时权值增加到十倍
}
三.贪心
/*本例子是用来说明基础算法之递归
* 题目:A公司每次都是将连续5个月的盈或亏的总和做一次性的公布。已知所有月的 * 盈利固定为s,而亏损月的亏损固定为d
* 编写一个程序,确定MS公司是否盈利,若盈利的话,计算可能盈利的最大值。
* 输入为:两个正整数s和d。
* 输出为:若盈利的话,输出可能的盈利最大值;若亏损的话,输出Deficit
* 解题思路:首先每连续5个月中至少有哪几个月是亏损的是可以计算出来的假设5个 * 月至少亏损3个月,那么对于前五个月来说,亏损的月份必定是3、4、5月,这样才* 能减少出现更多的亏损月 */
#include <iostream>
#include <cstdio>
using namespace std;
int main(){
int s,d;
while(scanf("%d%d",&s,&d)!=EOF){
int i,ans;
for(i=1;i<=5;i++)
if(s*(5-i)-d*i<0)
break;
if(i==4)
ans=3*s-9*d;
else
ans=s*(12-2*i)-d*2*i;
if(i==5||ans<0)
printf("Deficit\n");
else
printf("%d\n",ans);
}
}
/* 1 2 3 4 5 6 7 ||8 9 10 11 12
* 1 * *
* 2 * * * *
* 3 * * * * * *
* 4 * * * * * * * * *
* 5 * * * * * * * * * * * *
* */
/*本例子是用来说明基础算法之递归
* 题目:A公司每次都是将连续5个月的盈或亏的总和做一次性的公布。已知所有月的 * 盈利固定为s,而亏损月的亏损固定为d
* 编写一个程序,确定MS公司是否盈利,若盈利的话,计算可能盈利的最大值。
* 输入为:两个正整数s和d。
* 输出为:若盈利的话,输出可能的盈利最大值;若亏损的话,输出Deficit
* 解题思路:首先每连续5个月中至少有哪几个月是亏损的是可以计算出来的假设5个 * 月至少亏损3个月,那么对于前五个月来说,亏损的月份必定是3、4、5月,这样才* 能减少出现更多的亏损月 */
#include <iostream>
#include <cstdio>
using namespace std;
int main(){
int s,d;
while(scanf("%d%d",&s,&d)!=EOF){
int i,ans;
for(i=1;i<=5;i++)
if(s*(5-i)-d*i<0)
break;
if(i==4)
ans=3*s-9*d;
else
ans=s*(12-2*i)-d*2*i;
if(i==5||ans<0)
printf("Deficit\n");
else
printf("%d\n",ans);
}
}
/* 1 2 3 4 5 6 7 ||8 9 10 11 12
* 1 * *
* 2 * * * *
* 3 * * * * * *
* 4 * * * * * * * * *
* 5 * * * * * * * * * * * *
* */
四.枚举
/*本例子是用来说明基础算法之枚举
* 题目:输入多组数据,每组包括两行,第一行输入一个n(<100),表示小A有n根木* 根,接着一行有n个整数(<1000),表示木棍的长度(长度从小到大给出)
* 输出可以组成面积最大的直角三角形的面积,且保留三位有效数字,如果不能组成 *输出“My God!"
* 解题思路:把所有直角三角形的可能性枚举出来,利用题目中木棍的长度从大到小 * 排列,而斜边最长这一特征 */
#include <stdio.h>
#include <stdlib.h>
int main(){
int i,j,k;
double ans;
int n;
int len[110];
while(scanf("%d",&n)!=EOF){
for(i=1;i<=n;i++)
scanf("%d",&len[i]);
ans=-1;
for(i=1;i<=n;i++){//枚举最短木棍
for(j=i+1;j<=n;j++){//枚举第二长的木棍
for(k=j+1;k<=n;k++){//枚举最长的木棍
if(len[i]*len[i]+len[j]*len[j]==len[k]*len[k]){//如果是直角三角形
if(0.5*len[i]*len[j]>ans)//取最优解
ans=0.5*len[i]*len[j];
}
}
}
}
if(ans==-1)
printf("My God!\n");
else
printf("%.3lf\n",ans);
}
return 0;
}
以上为基础算法,看来似乎简单易懂,但其中算法的思想却不可以忽略,在以后的许多问题中都会有体现。掌握算法的核心思想确实有必要。在后面,我会更新出相应的实例。
/*本例子是用来说明基础算法之枚举
* 题目:输入多组数据,每组包括两行,第一行输入一个n(<100),表示小A有n根木* 根,接着一行有n个整数(<1000),表示木棍的长度(长度从小到大给出)
* 输出可以组成面积最大的直角三角形的面积,且保留三位有效数字,如果不能组成 *输出“My God!"
* 解题思路:把所有直角三角形的可能性枚举出来,利用题目中木棍的长度从大到小 * 排列,而斜边最长这一特征 */
#include <stdio.h>
#include <stdlib.h>
int main(){
int i,j,k;
double ans;
int n;
int len[110];
while(scanf("%d",&n)!=EOF){
for(i=1;i<=n;i++)
scanf("%d",&len[i]);
ans=-1;
for(i=1;i<=n;i++){//枚举最短木棍
for(j=i+1;j<=n;j++){//枚举第二长的木棍
for(k=j+1;k<=n;k++){//枚举最长的木棍
if(len[i]*len[i]+len[j]*len[j]==len[k]*len[k]){//如果是直角三角形
if(0.5*len[i]*len[j]>ans)//取最优解
ans=0.5*len[i]*len[j];
}
}
}
}
if(ans==-1)
printf("My God!\n");
else
printf("%.3lf\n",ans);
}
return 0;
}