文章目录
注:本文代码为 C语言 和 C++ 结合
一、枚举
1.完美立方
#include<stdio.h>
int main() {
int N,a,b,c,d;
scanf("%d",&N);
for(a=2; a<=N; a++)
for(b=2; b<a; b++)
for(c=b; c<a; c++)
for(d=c; d<a; d++)
if(a*a*a==b*b*b+c*c*c+d*d*d)
printf("Cube = %2d, Triple = (%d,%d,%d)\n",a,b,c,d);
return 0;
}
2.生理周期
#include<iostream>
#include<cstdio>
using namespace std;
#define N 21252
int main() {
int p,e,i,d,caseNo=0;
while(cin>>p>>e>>i>>d && p!=-1) {
caseNo++;
int k;
for(k=d+1; (k-p)%23; k++);
for(; (k-e)%28; k+=23);
for(; (k-i)%33; k+=23*28);
cout<<"Case"<<caseNo<<" : the next triple peak occurs in "<<k-d<<" day"<<endl;
}
return 0;
}
3.称硬币
补充:C++中bool函数的学习
//C++中bool函数的返回值是 true or false
#include<iostream>
using namespace std;
bool cmp(int a,int b){
if(a>b) return true;
return false;
}
int main(){
int a,b;
cin>>a>>b;
if(cmp(a,b)) cout<<"a > b"<<endl;//若返回值为 true,则执行该if语句
else cout<<"a < b"<<endl;
return 0;
}
称硬币 代码如下:
#include<iostream>
#include<cstring>
using namespace std;
char Left[3][7];
char Right[3][7];
char result[3][7];
bool IsFake(char c,bool light);
//light为真->假设假币为轻
// 假->假设假币为重
int main() {
int t,i;
char c;
cin>>t;
while(t--) {
for(i=0; i<3; i++) cin>>Left[i]>>Right[i]>>result[i];
//开始枚举:
for(c='A'; c<='L'; c++) {
if(IsFake(c,true)) {
cout<<c<<" is the counterfeit coin and it is light.";
break;
} else if(IsFake(c,false)) {
cout<<c<<" is the counterfeit coin and it is heavy.";
break;
}
}
}
return 0;
}
bool IsFake(char c,bool light) {
for(int i=0; i<3; i++) {
char *pLeft,*pRight;
if(light) {
pLeft=Left[i];
pRight=Right[i];
} else {
pLeft=Right[i];
pRight=Left[i];
}
switch(result[i][0]) {//天平右边的情况
case'u': //此时c应该在右边
if(strchr(pRight,c) == NULL)
return false;
break;
case'e': //此时c不应该出现在两边
if(strchr(pRight,c) || strchr(pLeft,c))
return false;
break;
case 'd': //此时c应该出现在左边
if(strchr(pLeft,c) == NULL)
return false;
break;
}
}
return true;
}
输入样例:
1
ABCD EFGH even
ABCI EFJK up
ABIJ EFGH even
输出:
K is the counterfeit coin and it is light.
4.熄灯问题
考虑“局部”
在这里插入代码片
二、递归
作用:
(1)替代多重循环
(2)解决本来就是用递归形式定会的问题
(3)将问题分解为更小的子问题进行求解
1.求n的阶乘
#include<stdio.h>
int Fact(int n){
if(n==0) return 1;
return n*Fact(n-1);
}
int main(){
int n;
scanf("%d",&n);
printf("%d",Fact(n));
return 0;
}
2.汉诺塔问题
#include<stdio.h>
void Hanoi(int n,char a,char b,char c){
if(n==1){
printf("%c -> %c\n",a,c);
return ;
}
Hanoi(n-1,a,c,b);
printf("%c -> %c\n",a,c);
Hanoi(n-1,b,a,c);
return ;
}
int Hanoi_step(int m){
if(m<=1) return 1;
return 2*Hanoi_step(m-1)+1;
}
int main(){
int n,count=0;
scanf("%d",&n);
Hanoi(n,'A','B','C');
count=Hanoi_step(n);
printf("需要移动 %d 次\n",count);
return 0;
}
3.N皇后问题
#include<stdio.h>
#include<math.h>
int N;
int queenPos[100];
//用来存放算好的皇后位置。最左上角是(0,0)
void NQueen(int k);
int main() {
scanf("%d",&N);
NQueen(0);
return 0;
}
void NQueen(int k) {
int i;
if(k==N) { //N个皇后都摆好了
for(i=0; i<N; i++)
printf("%d ",queenPos[i]+1);
printf("\n");
return ;
}
for(i=0; i<N; i++) { //逐个尝试第k个皇后的位置
int j;
for(j=0; j<k; j++) {
//和已经摆好的k个皇后比较,看是否冲突
if(queenPos[j]==i //看两个皇后的列是不是相同
|| abs(queenPos[j]-i) == abs(k-j)) //看两个皇后是不是在同一对角线
break; //冲突,测试下一个位置
}
if(k==j) { //当前选的位置 i 不冲突
queenPos[k]=i; //姜第k个皇后摆放在i上
NQueen(k+1);
}
}
}
4.逆波兰表达式
atof() 函数是C++的库函数,能将字符串转换成浮点类型的数字。
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
double exp() {
char s[100];
cin>>s;
switch(s[0]) {
case '+' :
return exp() + exp();
case '-' :
return exp() - exp();
case '*' :
return exp() * exp();
case '/' :
return exp() / exp();
default :
return atof(s);
break;
}
}
int main() {
printf("%lf",exp());
return 0;
}
5.表达式计算
补充:cin.peek() 和 cin.get() 的使用
cin.peek():看一个字符,不取走
cin.get():从输入中取走该字符
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
int factor(); //读入一个因子,并返回其值
int term(); //读入一项,并返回其值
int expression(); //读入一个表达式,并返回其值
int main() {
cout<<expression()<<endl;
return 0;
}
int factor() {
int result=0;
char c=cin.peek();
if( c == '(' ) {
cin.get();
result=expression();
cin.get();
} else {
while(isdigit(c)) { //如果是数字
result=10*result + c-'0'; //从高位开始读取,然后累加
cin.get();
c=cin.peek(); //不知道有几位数字,继续读取
}
}
return result;
}
int term() {
int result=factor(); //求第一个因子的值
while(true) {
char op=cin.peek();
if( op == '*' || op == '/' ) {
cin.get();
int value=factor();
if( op == '*' ) result*=value;
else result/=value;
} else break;
}
return result;
}
int expression() {
int result=term(); //求第一项的值
bool more=true;
while(more) {
char op=cin.peek(); //看一个字符,不取走
if( op == '+' || op == '-' ) {
cin.get(); //从输入中取走该字符
int value=term();
if( op == '+' ) result+=value;
else result-=value;
} else more=false;
}
return result;
}
6.爬楼梯
涉及斐波那契数列
C语言 代码:
#include<stdio.h>
int F(int n){
if( n == 1 ) return 1;
if( n == 2 ) return 2;
return F(n-1) + F(n-2);
}
int main(){
int n;
while( (scanf("%d",&n)) != EOF ){
printf("%d\n",F(n));
}
return 0;
}
C++ 代码:
#include<iostream>
using namespace std;
int F(int n){
if( n == 1 ) return 1;
if( n == 2 ) return 2;
return F(n-1) + F(n-2);
}
int main(){
int n;
while( cin>>n ){
cout<<F(n)<<endl;
}
return 0;
}
7.放苹果
#include<stdio.h>
int f(int i,int k){
if(i<k) return f(i,i);
if(i==0) return 1;
if(k==0) return 0;
return f(i,k-1)+f(i-k,k);
}
int main(){
int n,i,j,k;
scanf("%d",&n);
for(i=0;i<n;i++){
scanf("%d %d",&j,&k);
printf("%d",f(j,k));
}
return 0;
}
8.算24
#include<iostream>
#include<cmath>
using namespace std;
#define eps 1e-6
double a[5];
bool isZero(double x) {
return fabs(x)<=eps;
}
bool count24(double a[],int n) {
if(n==1) {
if(isZero(a[0] - 24)) return true;
else return false;
}
double b[5];
for(int i=0; i<n-1; i++)
for(int j=i+1; j<n; j++) {
int m=0; //还剩下 m 个数,m = n-2
for(int k=0; k<n; k++)
if(k!=i && k!=j) b[m++]=a[k]; //把其余的数放入b[]
//加
b[m]=a[i]+a[j];
if(count24(b,m+1)) return true;
//减 注意两种情况
b[m]=a[i]-a[j];
if(count24(b,m+1)) return true;
b[m]=a[j]-a[i];
if(count24(b,m+1)) return true;
//乘
b[m]=a[i]*a[j];
if(count24(b,m+1)) return true;
//除 注意两种情况
if(!isZero(a[j])) {
b[m]=a[i]/a[j];
if(count24(b,m+1)) return true;
}
if(!isZero(a[i])) {
b[m]=a[j]/a[i];
if(count24(b,m+1)) return true;
}
}
return false;
}
int main() {
while(1) {
for(int i=0; i<4; i++)
cin>>a[i];
if(isZero(a[0])) break;
if(count24(a,4)) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
三、二分算法
1.时间复杂度
时间复杂度的详解
2.二分查找函数
<1>.BinarySearch
#include<stdio.h>
int BinarySearch(int a[],int size,int p) {
int L=0;
int R=size-1;
while(L<=R) {
int mid = L + (R-L)/2;
if(p==a[mid]) return mid;
else if(p>a[mid]) L=mid+1;
else R=mid-1;
}
return -1;
}
int main(){
int size,p;
scanf("%d %d",&size,&p);
int a[size];
for(int i=0;i<size;i++){
scanf("%d",&a[i]);
}
printf("%d",BinarySearch(a,size,p)+1); //下标从 1 开始算
return 0;
}
<2>.LowerBound
#include<stdio.h>
int LowerBound(int a[],int size,int p) {
int L=0;
int R=size-1;
int lastPos=-1; //到目前为止找到的最优解
while(L<=R) {
int mid= L + (R-L)/2;
if(a[mid]>=p)
R=mid-1;
else {
lastPos=mid;
L=mid+1;
}
}
return lastPos;
}
int main(){
int size,p;
scanf("%d %d",&size,&p);
int a[size];
for(int i=0;i<size;i++){
scanf("%d",&a[i]);
}
printf("%d",LowerBound(a,size,p)+1); //下标从 1 开始算
return 0;
}
3.二分法求方程的根
#include<stdio.h>
#include<math.h>
#define eps 1e-6
double f(double x) { //定义待求函数
return x*x*x-5*x*x+10*x-80;
}
//先经过简单求导运算,求出根的大致范围为[0,100]
int main() {
double root=0,x1=0,x2=100,y=0;
int count=1; //计算求根的次数
root=x1+(x2-x1)/2;
y=f(root);
while(fabs(y)>eps) {
if(y>0) x2=root;
else x1=root;
root=x1+(x2-x1)/2;
y=f(root);
count++;
}
printf("%.8lf\n",root);
printf("%d",count);
return 0;
}
4.例题
<1>.寻找指定和的整数对
二分法求解(解法二):
#include<iostream>
#include<algorithm>
using namespace std;
int Find(int a[],int size,int p) {
int L=0,R=size-1;
while(L<=R) {
int mid=L+(R-L)/2;
if(p==a[mid]) return mid;
else if(p>a[mid]) L=mid+1;
else R=mid-1;
}
return -1;
}
int main() {
int i,n,m,flag=0,p=0,r=0;
cin>>n>>m;
int a[n];
for(i=0; i<n; i++)
cin>>a[i];
sort(a,a+n);
for(i=0; i<n/2; i++) {
p=m-a[i];
if(Find(a,n,p)>0) {
r=Find(a,n,p);
cout<<a[i]<<" "<<a[r]<<endl;
flag=1;
}
}
if(flag==0) cout<<"No Answer!"<<endl;
return 0;
}
非二分法求解(解法三):
#include<iostream>
#include<algorithm>
using namespace std;
int main() {
int n,m;
cin>>n>>m;
int a[100000];
for(int i=0; i<n; i++)
cin>>a[i];
sort(a,a+n);
int i=0,j=n-1;
while(i<j){
if(m==a[i]+a[j]){
cout<<a[i]<<" "<<a[j]<<endl;
break;
}
else if(a[i]+a[j]>m) j--;
else i++;
}
if(i==j) cout<<"No Answer!"<<endl;
return 0;
}
输入样例:
10 20
2 5 1 11 8 22 20 18 19 27
输出样例 :
2 18
<2>.Agressive cows
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MaxD 1000000000
int N, C;
bool isRight(int d, int pos[]) {
int cnt=1, pre=pos[0] ; //cnt计数放好位置的牛 初始时第一头牛放在第一个位置
for(int i=1; i<N; i++) { //for循环开始放置第二头牛(index=1)
if(pos[i]>=pre+d) {
pre=pos[i]; //放在第i个位置 更新计数
cnt++;
}
if(cnt==C) return true; //所给的牛都按照距离d放入牛舍了
}
return false;
}
int main() {
int left, right, d, max;
cin>>N>>C;
int pos[N];
for(int i=0; i<N; i++)
cin>>pos[i];
sort(pos,pos+N);//对牛舍序号排序
left=0;
right=MaxD/C;
while(left<=right) { //二分遍历所有可能的距离,区间为[0,1000000000/c]
d=(left+right)/2;
if(isRight(d,pos)) {
max=d;
left=d+1;
} else
right=d-1;
}
cout<<max;
return 0;
}
输入样例:
5 3
1
2
8
4
9
输出样例:
3
四、分治
1.基本概念
将整个问题分解成若干小问题后再分而治之。如果分解得到的子问题相对来说还是太大,则可反复使用分治策略将这些子问题分成更小的同类型子问题,直至产生方便求解的子问题,必要时逐步合并这些子问题的解,从而得到问题的解。
分治算法可以由递归过程来表示,因为分治法就是一种找大规模问题与小规模问题关系的方法,是递归设计的一种具体策略。
2.归并排序
#include<iostream>
using namespace std;
void Merge(int a[],int s,int m,int e,int b[]) {
//将数组a[s,m]和a[m+1,e]合并到b,并保证b有序,然后再拷贝回a[s,m]
int pb=0,p1=s,p2=m+1;
while(p1<=m &&p2<=e) {
if(a[p1]<a[p2])
b[pb++]=a[p1++];
else b[pb++]=a[p2++];
}
while(p1<=m) b[pb++]=a[p1++];
while(p2<=e) b[pb++]=a[p2++];
for(int i=0; i<e-s+1; i++)
a[s+i]=b[i];
}
void MSort(int a[],int s,int e,int b[]) {
if(s<e) {
int m=s+(e-s)/2;
MSort(a,s,m,b);
MSort(a,m+1,e,b);
Merge(a,s,m,e,b);
}
}
int main() {
int i,size,a[10000],b[10000];
cin>>size;
for(i=0; i<size; i++)
cin>>a[i];
MSort(a,0,size-1,b);
for(i=0; i<size; i++)
cout<<a[i]<<" ";
cout<<endl;
return 0;
}
3.快速排序
#include<iostream>
using namespace std;
void swap(int *a,int *b) {
int tmp=*a;
*a=*b;
*b=tmp;
}
void QuickSort(int a[],int s,int e) {
if(s>=e) return ; //递归返回的出口
int k=a[s];
int i=s,j=e;
while(i!=j) {
while(j>i && a[j]>=k)
j--;
swap(&a[i],&a[j]); //当 a[j]<k 时,交换 a[i] 和 a[j] , 换完 a[j]=k
while(i<j && a[i]<=k)
i++;
swap(&a[i],&a[j]); //当 a[i]>k 时,交换 a[i] 和 a[j]
}//处理完后a[i]=k
QuickSort(a,s,i-1);
QuickSort(a,i+1,e);
}
int main() {
int i,size;
cin>>size;
int a[size];
for(i=0; i<size; i++)
cin>>a[i];
QuickSort(a,0,size-1);
for(i=0; i<size; i++)
cout<<a[i]<<" ";
cout<<endl;
return 0;
}
输入样例:
10
93 27 30 2 8 12 2 8 30 89
输出样例:
2 2 8 8 12 27 30 30 89 93
4.例题
<1>.输出前m大的数
#include<iostream>
#include<algorithm>
using namespace std;
void Quick(int a[],int l, int r, int m) { //快速排序
if(l>=r) return ; //递归返回的出口
int k=a[l];
int i=l,j=r;
while(i!=j) {
while(j>i && a[j]>=k)
j--;
swap(a[i],a[j]);
while(i<j && a[i]<=k)
i++;
swap(a[i],a[j]);
}
Quick(a,i+1,r,m); //将 k 右边的数都排序
if((r-j)<m) Quick(a,l,i-1,m); //如果 k 右边的数小于 m ,将 k 左边的数也排序
}
int main() {
int n,m,i;
cin>>n;
int a[n];
for(i=0; i<n; i++)
cin>>a[i];
cin>>m;
Quick(a,0,n-1,m);
for(i=n-1; i>=(n-m); i--) {
cout<<a[i]<<endl;
}
return 0;
}
输入样例:
10
4 5 6 9 8 7 1 2 3 0
6
输出样例:
9
8
7
6
5
4
<2>.求排列的逆序数
#include<iostream>
#include<algorithm>
using namespace std;
void Merge(int a[],int l,int mid,int r,int b[]) {
//将左半边和右半边分别按从大到小排序 (归并排序)
int pb=0,p1=l,p2=mid+1;
while(p1<=mid && p2<=r) {
if(a[p1]>=a[p2])
b[pb++]=a[p1++];
else
b[pb++]=a[p2++];
}
while(p1<=mid)
b[pb++]=a[p1++];
while(p2<=r)
b[pb++]=a[p2++];
for(int i=0; i<r-l+1; i++)
a[l+i]=b[i];
}
int MSortCount(int a[],int l,int r,int b[]) {
//计算逆序数有几对
int cnt=0;
if(l<r) {
int mid=l+(r-l)/2;
cnt+=MSortCount(a,l,mid,b); //左半边有的逆序数对
cnt+=MSortCount(a,mid+1,r,b); //右半边有的逆序数对
//计算 两边各取一个数构成的逆序数对:
int i=l,j=mid+1;
while(i<=mid && j<=r) {
if(a[i]<=a[j])
j++;
else {
cnt+=r-j+1;
i++;
}
}
Merge(a,l,mid,r,b);
}
return cnt;
}
int main() {
int n,i;
cin>>n;
int a[n],b[10000];
for(i=0; i<n; i++)
cin>>a[i];
cout<<MSortCount(a,0,n-1,b)<<endl;
return 0;
}
输入样例:
6
2 6 3 4 5 1
8
五.动态规划
1.数字三角形
法一 : 递归实现
#include<iostream>
#include<algorithm>
using namespace std;
#define MAX 101
int D[MAX][MAX];
int n;
int maxSum[MAX][MAX];
int MaxSum(int i,int j) {
if(maxSum[i][j]!=-1)
return maxSum[i][j]; //避免重复计算
if(i==n)
maxSum[i][j]=D[i][j]; //在最后一行,直接返回 D[i][j]
else { //用递归实现 从 底部 向上寻找最大和
int x=MaxSum(i+1,j);
int y=MaxSum(i+1,j+1);
maxSum[i][j]=max(x,y) + D[i][j];
}
return maxSum[i][j];
}
int main() {
int i,j;
cin>>n;
for(i=1; i<=n; i++)
for(j=1; j<=i; j++) {
cin>>D[i][j];
maxSum[i][j]=-1; //先赋值 -1 ,用于避免重复计算
}
cout<<MaxSum(1,1)<<endl;
return 0;
}
法二 : 递推实现(空间优化)
#include<iostream>
#include<algorithm>
using namespace std;
#define MAX 101
int D[MAX][MAX];
int n;
int * maxSum;
int main() {
int i,j;
cin>>n;
for(i=1; i<=n; i++)
for(j=1; j<=i; j++)
cin>>D[i][j];
maxSum=D[n]; // maxSum 指向第 n 行
for(i=n-1; i>=1; i--)
for(j=1; j<=i; j++)
maxSum[j]=max(maxSum[j],maxSum[j+1]) + D[i][j];
cout<<maxSum[1]<<endl;
return 0;
}
输入样例:
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出样例:
30
2.最长上升子序列
补充:
max_element()
用于从范围 [first, last) 中获取最大值的元素。
min_element()
用于从范围 [first, last) 中获取最小值的元素。
两个函数的用法:
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int ans = 0;
int arr[] = { 1, 2, 3, 4, 2 };
ans= *max_element(arr + 0, arr + 5);
cout << "max element of the array:" << ans<< endl;
ans= *min_element(arr + 0, arr + 5);
cout << "min element of the array:" << ans<< endl;
return 0;
}
例题代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN=1010;
int a[MAXN];
int maxLen[MAXN];
int main() {
int n,i,j;
cin>>n;
for(i=1; i<=n; i++) {
cin>>a[i];
maxLen[i]=1;
}
for(i=2; i<=n; i++) //上升子序列的 "终点"(最右边)
for(j=1; j<i; j++) //遍历"终点"左边的数
if(a[i]>a[j])
maxLen[i]=max(maxLen[i],maxLen[j]+1);
cout<<*max_element(maxLen+1,maxLen+1+n);
return 0;
}
输入样例:
7
1 7 3 5 9 4 8
输出样例:
4
3.最长公共子序列
#include<iostream>
#include<cstring>
using namespace std;
char s1[1000],s2[1000];
int MaxLen[1000][1000]; //用二维数组来存储公共子序列的长度
int main() {
while(cin>>s1>>s2) {
int len1=strlen(s1);
int len2=strlen(s2);
int i,j;
for(i=0; i<=len1; i++)
MaxLen[i][0]=0; //先将长度初始为 0
for(j=0; j<=len2; j++)
MaxLen[0][j]=0;
for(i=1; i<=len1; i++)
for(j=1; j<=len2; j++)
if(s1[i-1]==s2[j-1]) //字符串数组下标从 0 开始算
MaxLen[i][j]=MaxLen[i-1][j-1]+1;
else
MaxLen[i][j]=max(MaxLen[i][j-1],MaxLen[i-1][j]);
cout<<MaxLen[len1][len2]<<endl;
}
return 0;
}
输入、输出样例:
abcfbc abfcab
4
programming contest
2
abcd mnp
0