基础算法
二分答案
最大值输出R,最小值输出L
求最大值mid=x时,去掉左半边
求最小值mid=x时,去掉右半边
check函数根据题意写
#include<bits/stdc++.h>
using namespace std;
/*核心代码*/
int A[10010]; //一定要为有序数列才可以使用二分
int n,m; //n为数组容量 ,m为所要查找的数;
/*寻找单个数值的二分*/
int find(int left,int right,int x){ //左右范围,x为所要查找的数
while(left<=right){ //当左右边界相等时不结束循环,下一次循环相等查找同一个数。
int mid=left+(right-left)/2;
if(A[mid]==x){
return mid;
}
else if(A[mid]>x) right=mid-1;
else left=mid+1;
}
return -1;
}
/*寻找左侧边界的二分查找 */
//最大值输出r,最小值输出l !!!!!
//求最大值mid=x时,去掉左半边
//求最小值mid=x时,去掉右半边
//右边界一直向左靠拢,直到相等,及到达满足条件的最左侧的位置
int zuo(int left,int right,int x){
while(left<=right){
int mid=left+(right-left)/2;
if(A[mid]>=x){
right=mid-1;
}
else{
left=mid+1; //最后一次循环结束时,left=right,此时left即为答案,right--小于left结束
}
}
if(A[left]==x) return left;
else return -1;
}
/*寻找右侧边界的二分查找*/
int you(int left,int right,int x){
while(left<=right){
int mid=left+(right-left)/2;
if(A[mid]<=x){
left=mid+1;
}
else{
right=mid-1;//最后一次循环结束时,left=right,此时right即为答案,left++大于right结束
}
}
if(A[right]==x) return right;
else return -1;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>A[i];
int a=find(1,n,m);
cout<<"该数的位置为:"<<a<<endl;
int b=zuo(1,n,m);
cout<<"该数的左边界为:"<<b<<endl;
int c=you(1,n,m);
cout<<"该数的右边界为:"<<c<<endl;
return 0;
}
/*
10 3
1 1 2 3 3 3 3 4 5 7
*/
快速排序
#include<iostream>
#include<algorithm>//用sort排序函数,max,min
#include<iomanip>// 设置输出位数
#include<cmath>// 使用系统函数,子函数
#include<string> //string a可以用length()判断字符长度
#include<cstdio> //使用gets()输入字符串
#include<cstdlib> //使用rand()求随机数
#include<string.h> //memset重置数组
int A[1000010];
// 快排 从小到大
void quickSort(int left,int right){
int i=left,j=right,base=A[left]; //取最左边的数为基准数
if(left>=right) return ; // 循环结束条件
while(i<j){
while(A[j]>=base&&i<j) //从右向左找第一个小于base数的位置
j--;
while(A[i]<=base&&i<j) //从左向右找第一个大于base数的位置
i++;
//交换两元素位置
if(i<j){ //如果i还在j左边,交换位置i和j上的数
swap(A[i],A[j]);
}
} //循环结束时 i=j
A[left]=A[i];
A[i]=base; //左端点和结束位置交换值。
quickSort(left,i-1);//递归左边
quickSort(i+1,right);//递归右边
}
using namespace std;
int main(){
int n;cin>>n;
for(int i=1;i<=n;i++) cin>>A[i];
quickSort(1,n);
for(int i=1;i<=n;i++){
if(i==n) cout<<A[i];
else cout<<A[i]<<" ";
}
cout<<endl;
return 0;
}
小细节合集
上下取整
//向下取整,向上取整
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
cout << floor(4)<<endl;
cout << floor(4.9)<<endl; //向下
cout << ceil(4.111) << endl;
cout << ceil(4.9) << endl;//向上
system("pause");
return 0;
}
场宽设置
//设置场宽
#include<iostream>
#include<iomanip>
using namespace std;
int a[3][3]={{-1,0,1},{0,1,10,},{1,-10,1}};
int main(){
for (int i=0;i<3;i++){
for (int j=0;j<3;j++) {
cout<<setw(5)<<a[i][j];
//设置场宽为5
}
cout<<endl;
}
}
前后全排列函数
#include <iostream>
#include <algorithm>
using namespace std;
int main(){
int A[5]={1,2,3,4,5};
next_permutation(A,A+5); //下一个全排列
for(int i=0; i<5; i++){
cout<<A[i]<<" ";
}
cout<<endl;
prev_permutation(A,A+5); //上一个全排列
for(int i=0; i<5; i++){
cout<<A[i]<<" ";
}
cout<<endl;
}
优先队列
top 访问队头元素
empty 队列是否为空
size 返回队列内元素个数
push 插入元素到队尾 (并排序)
emplace 原地构造一个元素并插入队列
pop 弹出队头元素
swap 交换内容
#include<iostream>
#include<queue>
//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;
//greater和less是std实现的两个仿函数
优先队列
// map 容器
#include<iostream>
#include<cmath>
#include<map>
using namespace std;
/*
map容器的定义
map映射容器的元素数据是由一个键值和一个映射数据组成的,数据类型随便定义
键值与映照数据之间具有一一映照的关系。且键值唯一。若键值重复,数据会被覆盖
*/
map<int,char>mp;
int main(){
//数组方式插入
mp[1]='a';
mp[1]='b';//key不允许重复,再次插入相当于修改value的值
mp[2]='a';
mp[3]='b';
mp[4]='c';
//insert()方式插入
mp.insert(map<int,char>::value_type(5,'d'));
//通过关键字key删除元素
mp.erase('b');
//输出容器大小
int s=mp.size();
//遍历输出map中的元素
map<int,char>::iterator it=mp.begin();
while(it!=mp.end())
{
cout<<"key:"<<it->first<<" ";
cout<<"value:"<<it->second<<endl;;
it++;
}
mp.clear();//清空容器
it=mp.find(1);//搜索键值为1的元素
/*若该键值存在,则返回该键值所在的
迭代器位置,不存在则返回end()迭代器位置
*/
}
/*
begin() 返回指向 map 头部的迭代器
clear() 删除所有元素
count() 返回指定元素出现的次数
empty() 如果 map 为空则返回 true
end() 返回指向 map 末尾的迭代器
erase() 删除一个元素
find() 查找一个元素
insert() 插入元素
key_comp() 返回比较元素 key 的函数
lower_bound() 返回键值>=给定元素的第一个位置
max_size() 返回可以容纳的最大元素个数
rbegin() 返回一个指向 map 尾部的逆向迭代器
rend() 返回一个指向 map 头部的逆向迭代器
size() 返回 map 中元素的个数
swap() 交换两个 map
upper_bound() 返回键值>给定元素的第一个位置
value_comp() 返回比较元素 value 的函数
*/
搜索
BFS 地图问题
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
char map[30][30]; //地图数组
bool v[30][30]; //访问数组
int mina=9999,flag=0,m,n,xq,yq,dx[]={1,-1,0,0},dy[]={0,0,1,-1}; //四个方向
struct point{
int x,y,step;
};
queue<point> r; //创建结构体队列 r
int main(){
cin>>m>>n;
memset(v,0,sizeof(v));
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
cin>>map[i][j];
if(map[i][j]=='@'){
xq=i; yq=j;
}
}
}
point start; //队列首元素
start.x=xq;
start.y=yq;
start.step=0;
r.push(start);
v[xq][yq]=1;
//核心代码
while(!r.empty()){ //当队列为空时结束
int x=r.front().x,y=r.front().y; //每次都扩展首元素的位置
if(map[x][y]=='*'){ //满足条件输出答案
flag=1;
if(r.front().step<mina) mina=r.front().step;
}
for(int i=0;i<4;i++){ //分别向四个方向扩展
int tx=x+dx[i];
int ty=y+dy[i];
if(!v[tx][ty]&&map[tx][ty]!='#'&&tx>0&&tx<=m&&ty>0&&ty<=n){//x范围为行数,y为列数 ,
// 如果可以扩展
point time; //创建一个临时结构体
time.x=tx;
time.y=ty;
time.step=r.front().step+1;
v[tx][ty]=1;
r.push(time); //把结构体放入队列尾部
}
}
r.pop(); //队列首元素扩展完毕后移出队列
}
if(flag==0) cout<<"-1"<<endl;
else cout<<mina<<endl;
return 0;
}
DFS
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
int w,h,x,y,ans=0;
char map[30][30];
int xx[4]={1,-1,0,0},yy[4]={0,0,1,-1},flag[30][30]; //flag为拜访数组
//xx和yy分别表示四个方向
void dfs(int x,int y){
flag[x][y]=1; //标识以走过
ans++;
for(int i=0;i<4;i++){ //向四个方向扩展
int tx=x+xx[i];
int ty=y+yy[i];
if(map[tx][ty]=='.'&& !flag[tx][ty]&&(tx>0&&tx<=w&&ty>0&&ty<=h)){//x范围为行数,y范围为列数
dfs(tx,ty); //递归扩展,直到满足条件或者全部扩展完成结束
}
}
}
int main(){
cin>>h>>w;
memset(flag,0,sizeof(flag));
memset(map,0,sizeof(map));
for(int i=1;i<=w;i++)
for(int j=1;j<=h;j++){
cin>>map[i][j];
if(map[i][j]=='@'){
x=i; y=j; //保存起点
}
}
dfs(x,y);
cout<<ans<<endl;
return 0;
}
深搜回溯
在数据较小时,爆搜所有可能性,典型问题全排列
#include<iostream>
#include<cmath>
#include<iomanip>
using namespace std;
int n,A[10],v[10],ans[10];
void printf(){ //输出函数
for(int i=1;i<=n;i++)
cout<<setw(5)<<ans[i]; cout<<endl;
return ;
}
void dfs(int a){
if(a>n){
printf();
return ;
}
for(int i=1;i<=n;i++){
if(v[i]==0){
v[i]=1;
ans[a]=A[i];
a++; //往下继续走
dfs(a);
a--; //走不动时原路返回
v[i]=0;
ans[a]=0;
}
}
return ;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) A[i]=i;
dfs(1);
return 0;
}
树
并查集
#include<iostream>
#include<cmath>
using namespace std;
int fa[10010]; //fa表示每个元素的爹
int find(int x){ //递归找爹函数 ,找最辈分最高的爹
if(fa[x]==x) return fa[x]; //自己就是自己的爹,返回自己
else{
fa[x]=find(fa[x]); //
return fa[x];
}
}
void hb(int x,int y){ //合并函数,两个数输入后以为父子 ,需要认同一个爹
int xx=find(x);
int yy=find(y);
if(xx==yy) return ;
else{
fa[yy]=xx;
}
}
int main(){
int n,m; cin>>n>>m;
for(int i=1;i<=n;i++) fa[i]=i;
while(m--){
int a,b,c; cin>>a>>b>>c;
if(a==1){ //b和c合并到一个集合
hb(b,c);
for(int i=1;i<=n;i++) cout<<fa[i]<<" ";
cout<<endl;
}
else{ //判断是否在一个集合中
if(find(b)==find(c)) cout<<"Y"<<endl;
else cout<<"N"<<endl;
}
}
return 0;
}
最小生成树
kruskal
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,fa[5010],ans=0,sum=0;
//存放每条边的起点终点,权值
struct side{
int s,e,l;
}A[200010];
int find(int x){ //循环找爹函数,不多解释
if(x==fa[x]) return x;
else{
fa[x]=find(fa[x]);
return fa[x];
}
}
void hb(int x,int y){ //第二个人认第一个人做爹函数
int xx=find(x);
int yy=find(y);
if(xx==yy) return ;
else{
fa[yy]=xx;
}
return ;
}
bool cmp(side a,side b){
if(a.l<b.l) return 1;
else return 0;
}
void kruskal(){
for(int i=0;i<m;i++){
int fa1=find(A[i].s),fa2=find(A[i].e);
if(fa1==fa2) continue; // 判断两个点是否连通,若连通 则不需要此边
ans=max(ans,A[i].l);
sum++;
//已选择边的个数自加一,当已选择边数为点的个数减一时最小生成树完成
hb(fa1,fa2);
if(sum==n-1) break;
}
}
int main(){
cin>>n>>m;
for(int i=0;i<m;i++){
cin>>A[i].s>>A[i].e>>A[i].l;
}
for(int i=1;i<=n;i++){
fa[i]=i;
}
sort(A,A+m,cmp); //按照边的权值排序
kruskal();
cout<<ans<<endl;
return 0;
}
Prim
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const long long maxn=1e4+5;
const long long inf=1e9+5;
long long n,x[maxn],y[maxn],vis[maxn];
//x和y存点的坐标,vis存某个点是否已在最小生成树中
double d[maxn],ans;
//d存放已生成的树到其他点距离,每次添加最近的点到树中
double get(long long i,long long j){
return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
void prim(){
for(long long i=1;i<=n;i++) d[i]=get(1,i);
/*在prim中,我们d数组表示的是第i个点到图中已经有的所有的点的最短距离,
因为用二维存距离的话会直接炸空间,所以我们用的时候在去处理;*/
d[0]=inf;
for(long long i=1;i<n;i++){ //只需要n-1条边
long long xi=0;
for(long long j=1;j<=n;j++){
if(!vis[j]&&(xi==0||d[j]<d[xi])) xi=j;//找到距离最近的那个点
}
vis[xi]=1;//往树上加一个点
ans+=d[xi];
for(long long j=1;j<=n;j++){
if(!vis[j]) d[j]=min(d[j],get(xi,j));//更新点距离部分
}
}
}
int main(){
scanf("%lld",&n);
for(long long i=1;i<=n;i++){
scanf("%lld%lld",&x[i],&y[i]);
}
vis[1]=1; //初始点,用此点更新到其他点的距离
prim();
printf("%.2lf",ans);//输出答案
return 0;
}
DP
背包问题
背包
//w重量,v价值
// 01
for (int i = 1; i <= n; i++)
for (int j = m; j >= w[i]; j--)
f[j] = max(f[j], f[j - w[i]] + v[i]);
//完全
for (int i = 1; i <= n; i++)
for (int j = w[i]; j <= m; j++)
f[j] = max(f[j], f[j - w[i]] + v[i]);