- 沈舒岩 的博客
学术
- @ 2026-7-25 12:13:19
食用注意事项:部分代码并未经过编译请以编译后为准,由于我的输入法有非常大的问题,可能在部分位置出现了奇奇怪怪的字符,请告知我更改,非常感谢,如果有没有写全的地方也可以提供学术支持,我会将您的名字放在感谢版里。
算法不好就背板子。
Day1萌萌STL
讲的是set和map还有一大堆的函数/kk
说实话set&map之前在xes听过一次怎么感觉讲的完全是俩个东西哇而且我们xes完全没有讲过auto这种高级的东西哇。
我们定义一个map<string,int>
插入:insert()
比如说
mp.insert({"xiaoming",20});
mp.insert(make_pair("xiaobei",15);
mp.["xiaotian"]=10;
元素访问
1.下标访问法
cout<<mp["xiaoming"]<<endl;
自动插值的特性:mp["beibei"]你没有给它赋值的话就有一个随机定值比如(beibei,0)
2.访问法
cout<<mp.at("xiaobei")<<endl;
3.find访问
mp.find("xiaotian");
auto it=mp.find();//科普一下auto
//aoto会自动匹配数据类型。
if(it!=mp.end()) //find函数找不到时返回end()
cout<<"键(下标)"<<it.first()<<endl;
cout<<"值:"<<it.second()<<endl;
删除
我们一般使用erase()来删除。
1.按key
mp.erase("xiaoming");
2.迭代器
mp.erase(it);
3.按区间删除
mp.erase(mp.begin(),mp.end())
容器遍历
累死了不更新了等我后续吧
&是取地址符
我们一般使用for循环遍历
简写版
for(aotu it:mp)
cout<<it.first()<<" "<<it.second.();
//c++14以上支持
Day搜索。
前面老师讲了一大堆总之今天就是把板子打打就行了。
爆搜
//二进制枚举qwq!板子开始qwq
for(int i=0;i<(1<<n);i++)
{
//枚举i的每一个二进制位是否被选
for(int j=0;j<n;j++)//枚举n个二进制位qwq
{
if(i>>j&1)
{
//开始操作
}
}
}
STL的函数。
bitset<>由于有了这个东西我们上面的爆搜就可以
//二进制枚举qwq!板子开始qwq
for(int i=0;i<(1<<n);i++)
{
bitset<114514>b(i)//把i转为二进制串
//枚举i的每一个二进制位是否被选
for(int j=0;j<n;j++)//枚举n个二进制位qwq
{
if(b[j])
{
//开始操作
}
}
}
b.count()表示统计二进制01的个数qwq。
关于定义常量的一些东西qwq
const是在程序里面发挥常量最用。
constexpr编译时发挥作用。(稍微快一点。)
感觉比快读优化TLE好
说实话你优化TLE还不如优化核心代码,加些快读真的没啥用(亲测。
萌萌dfs来之。搜索概念,用01串表示子集qwq
#include<bits/stdc++.h>
using namespace std;
//path 路径
void dfs(int depth,string p)
{
//1.结束条件
if(depth>n)
{
cout<<p<<"\n";
return;
}
//调用自己。(寻找子问题的解)
//不选
dfs(depth+1,p+"0");
//选
dfs(depth+1,p+"1");
}
int main()
{
//输入
dfs(1,"");
return 0;
}
如果你需要子集和,那么
#include<bits/stdc++.h>
using namespace std;
//path 路径
void dfs(int depth,int sum)
{
//1.结束条件
if(depth>n)
{
ans+=sum;
return ;
}
//调用自己。(寻找子问题的解)
//不选
dfs(depth+1,sum);
//选
dfs(depth+1,sum+=a[depth]);
}
int main()
{
//输入
dfs(1,0);
return 0;
}
next_permutation全排列函数,嗯对就是一定要把next_permutation放到do while里!
于是我们的全排列就变成了这个样子也是非常之简单
#include<bits/stdc++.h>
using namespace std;
const int N=114514;
long long n,a[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
a[i]=i;
}
do
{
for(int i=1;i<=n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}while(next_permutation(a+1,a+n+1));
return 0;
}
day3 dfs
1.dfs的概念 2.dfs的回溯
这里没有记下来……
1.地图---->二维表格---->二维数组
2.点---->坐标(行列)---->()坐标系!? 不是我刚刚想到坐标系老师就说到坐标系了?!
3.墙---->障碍物---->存在图里
4.起点&终点---->存在图里
5.已经走过的路径---->标记的数组记录vis[][]
6.方向---->偏移数组---->dx[]={1,0,0,-1} dy[]={0,1,-1,0}
提供一个很好用的dfs板子
lgP1605
可达P0429
#include<bits/stdc++.h>
using namespace std;
const int N=114;
long long n,m,a[N][N],t;
long long dx[]={1,0,0,-1},dy[]={0,1,-1,0};
long long vis[N][N],sx,sy,fx,fy;
long long qwq=0,x,y;
void dfs(int x,int y)
{
if(x==fx&&y==fy)
{
qwq++;
return;
}
for(int i=0;i<4;i++)
{
int nx=x+dx[i];
int ny=y+dy[i];
//判断下一个位置对不对(nx,ny)是否合理
if(nx<1||nx>n||ny<1||ny>m||a[nx][ny]||vis[nx][ny]) continue;
vis[nx][ny]=1;
dfs(nx,ny);
vis[nx][ny]=0;
}
}
int main()
{
cin>>n>>m>>t;
cin>>sx>>sy>>fx>>fy;
for(int i=1;i<=t;i++)
{
cin>>x>>y;
a[x][y]=1;
}
vis[sx][sy]=1;
dfs(sx,sy);
cout<<qwq;
return 0;
}
Day4 bfs
地图---->二维表格---->二维数组
点---->坐标(x,y)---->(行,列)
路径---->标记访问---->vis[][]
路径长度---->dist[][] distance:距离
方向---->偏移数组---->dx[]={1,-1,0,0} dy[]={0,0,1,-1}
猫猫又来提供经典板子了
#include<bits/stdc++.h>
using namespace std;
const int N=114;
long long dist[N][N],n,m;
char g[N][N];
long long dx[]={1,-1,0,0},dy[]={0,0,1,-1};
struct node
{
int x,y;
};
int bfs(int sx,int sy)
{
memset(dist,-1,sizeof(dist));//全部初始化为-1
queue<node>q;//队列,存格子
q.push({sx,sy});
dist[sx][sy]=1;
while(q.size())
{
auto u=q.front();//获取队头
q.pop();//删除队头
if(u.x==n&&u.y==m)
{
return dist[n][m];
}
for(int i=0;i<4;i++)//遍历4个风向
{
int nx=u.x+dx[i];
int ny=u.y+dy[i];
//不出界,不碰障碍,有没有访问
if(nx<1||nx>n||ny<1||ny>m) continue;
if(g[nx][ny]=='#') continue;
if(dist[nx][ny]!=-1) continue;
//走的路径等于下个位置+1
dist[nx][ny]=dist[u.x][u.y]+1;
q.push({nx,ny});
}
}
return -1;//找不到了
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>g[i][j];
}
}
cout<<bfs(1,1);
return 0;
}
Day5 图论
无向图:边没有具体指向关系
有向图:边有具体的指向关系
路径(自己上课学)
度数:
1.有向图:入度&出度
2.无向图:连接这个节点边的数量
无向图的度数之和=边数*2
有向图=(入度之和+出度之和)=边数*2
连通图:任意两个不同的顶点之间都存在至少一条路径
有n个节点无向连通图有至少n-1个边
有n个节点的有向连通图有至少n个边
完全图:图中任意两个顶点之间有且只有一条边
有n个节点无向完全图有等差数列求和公式条边
(?(n*(n-1)/2)
有n个节点的有向完全图有n*(n-1)个边
Day6 dp
主要就是推推动态转移方程之类的,板子
最长上升子序列:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
long long a[N],dp[N],n,ans;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
dp[i]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<i;j++)
{
if(a[j]<a[i])
{
dp[i]=max(dp[i],dp[j]+1);
}
}
ans=max(ans,dp[i]);
}
cout<<ans;
return 0;
}
最长公共子序列
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1005;
ll dp[N][N];
string a,b;
int main()
{
cin>>a>>b;
ll la=a.size();
ll lb=b.size();
for(int i=1;i<=la;i++)
{
for(int j=1;j<=lb;j++)
{
if(a[i-1]==b[j-1])
{
dp[i][j]=dp[i-1][j-1]+1;
}
else
{
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
}
cout<<dp[la][lb];
return 0;
}
状态机DP(并非状压)
0:未选最后一个店铺 (前一个店铺已选/未选)
1:选择最后一个店铺 (前一个店铺未选)
动态规划
1.状态表示f[i][0/1]
1.1集合走了i个店铺且当前位于j的所有选法
1.2属性看题目
2.状态计算集合划分
f[i][0]有两种情况
如果最后一步的前一步状态是1f[i-1][0]
反之就是f[i-1][1]
所以状态转移方程为f[i][0]=max(f[i][0],f[i-1][1])
如果是f[i][1]那么就是最后一步从0到1写出来就是f[i-1][0] f[i][1]=f[i-1][0]+w[i]其中w[i]表示第i家的现金
题目大盗阿福
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=114514;
ll n,w[N],f[N][2],t;
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>w[i];
}
memset(f,0,sizeof(f));
f[0][1]=-1e18;
f[0][0]=0;
for(int i=1;i<=n;i++)
{
f[i][0]=max(f[i-1][0],f[i-1][1]);
f[i][1]=f[i-1][0]+w[i];
}
cout<<max(f[n][0],f[n][1])<<endl;
}
int main()
{
cin>>t;
while(t--)
{
solve();
}
return 0;
}
环形DP解题方法:破环成链
8.1背包问题
01背包:一个物品最多只能选一次影响背包内能装的因素:?
动态规划:状态表示f[i][j]
集合&属性:
集合:从前i个物品中选且物品总体积<=j的所有选法
属性:Max
状态计算:集合划分
01背包问题模板
#include<bits/stdc++.h>
using namespace std;
const int N=114,M=250;
long long dp[N][M],v[N],w[N],n,m;
int main()
{
cin>>m>>n;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
if(j>=v[i])
{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
}
else
{
dp[i][j]=dp[i-1][j];
}
}
}
cout<<dp[n][m];
return 0;
}
完全背包
#include<bits/stdc++.h>
using namespace std;
const long long N=114,M=300;
long long dp[N][M],v[N],w[N],n,m;
int main()
{
cin>>m>>n;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
for(int k=0;k*v[i]<=j;k++)
{
dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
}
}
}
cout<<"max="<<dp[n][m];
return 0;
}
我们发现完全背包(优化后)和01背包的状态转移方程
完全dp[i][j-v[i]]+w[i]
01dp[i-1][j-v[i]]+w[i]
只有有区别一个是i-1一个是
还有啊,注意判断j<v[i]的情况
由于这种文章写长了以后输入法会卡我会再开一个写
优化一下问题!?
附优化代码
#include<bits/stdc++.h>
using namespace std;
const long long N=114,M=300;
long long dp[N][M],v[N],w[N],n,m;
int main()
{
cin>>m>>n;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
if(j>=v[i])
{
dp[i][j]=max(dp[i-1][j],dp[i][j-v[i]]+w[i]);
}
else
{
dp[i][j]=dp[i-1][j];
}
}
}
cout<<"max="<<dp[n][m];
return 0;
}