cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
return 0;
}
输入输出加速 ios::sync_with_stdio(false)
作用:
禁用 C 和 C++ 标准流的同步,使 cin/cout 速度接近 scanf/printf。
适用性:
必须用:当题目输入/输出数据量较大时(如超过 1e5 行)。
可不加:数据量小的题目(如输入仅几十行)。
注意事项:
开启后不能混用 cin 和 scanf(会导致输入顺序错乱)。若需同时使用 cin 和 getchar(),需手动刷新缓冲区(cin.ignore())。
解除 cin 与 cout 绑定 cin.tie(0)作用:
解除 cin 和 cout 的默认绑定,减少频繁刷新缓冲区的开销。
适用性:
仅在交替使用 cin 和 cout 时有效(如 cout << "Enter: "; cin >> x;)。
如果题目只需单向输入输出,效果不明显。
例外情况:
纯填空题:只需输出结果,无需处理输入时,可以省略加速代码。
需要 scanf/printf:若题目要求高精度格式化输入输出(如 %02d),直接使用 C 风格函数更便捷。交互题:蓝桥杯极少出现交互题,若遇到需根据题目要求调整。
2024年真题
小球反弹
cpp
#include <bits/stdc++.h>
using namespace std;
//求的是最短的路径,可以多弹几遍,就是弹回左上角后在弹出去。。。所以k1 k2要约分
int main()
{
const long long length=343720,width=233333,vx=15,vy=17;//实际上vx=15v,vy=17v
long long k11=15*width;
long long k22=17*length;//实际上应该是k1/k2=15w/17l,k11k22是k1k2没约分
long long g=gcd(k11,k22);
long long k1=k11/g;
long long k2=k22/g;
double total_distance=sqrt(pow(2*k1*length,2)+pow(2*k2*width,2));//平方根,倍数函数
printf("%.2f",total_distance);//输出保留两位小数
return 0;
}
1. 基本数学运算
函数 描述 示例
abs(x) 整数绝对值(<cstdlib>) abs(-5) → 5
fabs(x) 浮点数绝对值 fabs(-3.14) → 3.14
2. 幂与对数
函数 描述 示例
pow(x, y) x的y次方 pow(2, 3) → 8.0
sqrt(x) 平方根 sqrt(16.0) → 4.0
exp(x) e的x次方 exp(1.0) ≈ 2.71828
log(x) 自然对数(ln(x)) log(10.0) ≈ 2.30259
log10(x) 以10为底的对数 log10(100.0) → 2.0
R 格式
cpp
#include<bits/stdc++.h>
using namespace std;
int n,di=0;//di小数点的位置
string d;
int num[2000];//开大一点,进位的多
int main(){
cin>>n>>d;
int len=d.size()-1;
int temp=len;
for(int i=len;i>=0;i--){
if(d[i]=='.'){
di=len-i;//不是i,是len-i
continue;
}
num[len-temp]=d[i]-'0';//字符串转int,他也是这么转的
temp--;
}
while(n--){
for(int i=0;i<len;i++){
num[i]*=2;
}
for(int i=0;i<len;i++){
num[i+1]+=num[i]/10;//+=不是=
num[i]=num[i]%10;
}
if(num[len])len++;
}
if(num[di-1]>=5){
num[di]++;
for(int i=di+1;i<len;i++){
num[i+1]+=num[i]/10;
num[i]=num[i]%10;
}
if(num[len])len++;
}
for(int i=len-1;i>=di;i--){
cout<<num[i];
}
return 0;
}
1. 数字转字符/字符串
(1) 单个数字(0-9)转字符
int num = 7;
char c = num + '0'; // 数字转ASCII字符
std::cout << c; // 输出 '7'
(2) 多位数转字符串
方法1:使用std::to_string(C++11)
int num = 42;
std::string str = std::to_string(num); // "42"
2. 字符/字符串转数字
(1) 单个字符('0'-'9')转数字
char c = '9';
int num = c - '0'; // 字符转数字
std::cout << num; // 输出 9
(2) 字符串转数字
方法1:std::stoi(C++11)
std::string str = "42";
int num = std::stoi(str); // 42
3. 往字符串中追加数字字符
(1) 直接追加字符串形式的数字
std::string s = "Answer: ";
int num = 42;
s += std::to_string(num); // "Answer: 42"
(3) 逐个字符追加(适用于特殊格式)
std::string s = "ID";
int id = 7;
s.push_back('0' + id); // "ID7"(仅适用于0-9)
宝石组合
cpp
#include<bits/stdc++.h>
using namespace std;
//gcd(a,b,c)
const int N = 1e5 + 1;
int n;
int h[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
int a;
cin >> a;
h[a]++;
}
for (int s = 100000; s >= 1; s--) {
int cnt = 0, k = 0;
int ans[3];
for (int i = s; i <=100000; i += s) {
if (h[i]) {//没考虑到存在多个相同的闪光度
cnt+=h[i];
int a=h[i];
while(a--&&k<3){
ans[k++] = i;
}
if (cnt >= 3) {
cout << ans[0] << " " << ans[1] << " " << ans[2];
return 0;
}
}
}
}
return 0;
}
数字接龙(dfs,递归要好好弄弄)
cpp
#include<bits/stdc++.h>
using namespace std;
int dir[8][2] = { -1,0,-1,1,0,1,1,1,1,0,1,-1,0,-1,-1,-1 };
int n, k;
int m[11][11];
string path;
bool jx[11][11][11][11];
bool vis[11][11];
bool dfs(int x, int y) {
if (x == n - 1 && y == n - 1) {
return path.size() == n * n - 1;
}
for (int i = 0; i < 8; i++) {
int nextx = x + dir[i][0], nexty = y + dir[i][1];
if (nextx<0 || nextx>n-1 || nexty<0 || nexty>n-1 || vis[nextx][nexty]||
m[nextx][nexty] != (m[x][y] + 1) % k)continue;//0-n-1,>n-1不是>n
if (i % 2 == 1 && (jx[nextx][y][x][nexty] || jx[x][nexty][nextx][y]))continue;
vis[nextx][nexty] = true;
jx[x][y][nextx][nexty] = true;
path += i + '0';//往字符串里面加数字(转成字符)
if (dfs(nextx, nexty)) return true;
path.pop_back();//字符从字符串退出来
vis[nextx][nexty] = false;
jx[x][y][nextx][nexty] = false;
}
return false;
}
int main() {
cin >> n >> k;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> m[i][j];
}
}
vis[0][0] = true;
if (dfs(0, 0))cout << path;
else cout << -1;
return 0;
}
//用了bfs,返回类型错误,判断交叉只判断了一个方向,并且只有奇数方向才会交叉没想到,
//不知道string path放进去了还可以退出来,还有对于没有路径的情况也没写
//成功条件也只是n-1,n-1而已,没加上path。size==n*n-1
一、什么时候用 BFS?什么时候用 DFS?
- 优先使用 BFS 的场景
BFS 适用于需要 最短路径、最小步数、层级遍历 的问题,因为它一层一层地扩展,保证第一次找到解时就是最优解。
典型应用场景:
无权图的最短路径(如迷宫、网格问题)。
层级遍历(如二叉树的层序遍历、社交网络的朋友推荐)。
状态空间搜索(如八数码问题、单词接龙)。
扩散类问题(如腐烂的橘子、岛屿数量)。
- 优先使用 DFS 的场景
DFS 适用于 所有解、回溯、连通性问题、排列组合,因为它会深入搜索所有可能的分支。
典型应用场景:
回溯问题(如全排列、组合总和、N 皇后)。
连通性问题(如岛屿面积、图的连通分量)。
拓扑排序(如课程安排、任务调度)。
路径问题(如二叉树的所有路径、图的环路检测)。
二、如何确定 DFS/BFS 的返回类型?
- BFS 的返回类型
如果求最短路径 → 返回 int(步数)。
如果需要路径 → 返回 vector<Node*> 或 string(如迷宫路径)。
如果只是遍历 → 返回 void 或 vector<vector<T>>(如二叉树的层序遍历)。
- DFS 的返回类型
如果求所有解 → 返回 void,用参数 res 收集结果(如全排列)。
如果求是否存在解 → 返回 bool(如单词搜索)。
如果求单个解(如最长路径) → 返回 int 或自定义类型。
三、关键对比总结
场景 BFS DFS 最短路径 ✅ 最优解(第一次找到即最短) ❌ 可能找到非最短路径 所有解(回溯) ❌ 不适合 ✅ 适合 层级遍历 ✅ 天然适合 ❌ 需要额外记录深度 空间复杂度 ❌ 较高(队列存储所有节点) ✅ 较低(递归栈或手动栈) 适用数据结构 queue
递归
或stack
返回类型 int
(步数)、vector
(路径)void
(收集解)、bool
(存在性)四、如何选择?
求最短路径、最少操作次数? → BFS (如迷宫最短路径、单词接龙)
求所有可能的解、排列组合? → DFS + 回溯 (如全排列、N 皇后)
图的连通性问题? → DFS 或 BFS 均可 (如岛屿数量、朋友圈)
拓扑排序? → BFS(Kahn 算法)或 DFS(后序遍历反转) (如课程安排)
拔河 (前缀和)
cpp
// 在每次迭代中,代码删除的是所有以 i 为左端点的子数组的和(即 [i, i], [i, i+1], ..., [i, n])。
// 这些子数组的左端点都是 i,而之前迭代中已经删除了左端点为 1 到 i-1 的子数组的和。
// 因此,all_sums 中剩下的子数组的左端点都大于 i。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
multiset<ll> sum;//multiset 允许重复的 set唯一
int main() {
ios::sync_with_stdio(false);
cin.tie(0);//加速用的
cin >> n;
vector<ll> pre(n + 1, 0), num(n + 1);//vector初始化,放在cin>>n后面
for (int i = 1; i <= n; i++) {
cin >> num[i];
pre[i] = pre[i - 1] + num[i];
}
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
sum.insert(pre[j] - pre[i - 1]);//插入用insert
}
}
ll ans = LLONG_MAX;//long long 最大值
for (int i = 1; i < n; i++) {//枚举左区间右端点。右区间不能没有,所以是<n
for (int j = i; j <= n; j++) {
sum.erase(pre[j]-pre[i-1]);//删除和查找元素
}
for (int j = 1; j <= i; j++) {
ll left_sum = pre[i] - pre[j - 1];
auto it = sum.lower_bound(left_sum);//lower_bound,返回值和用法,找到第一个≥left_sum的元素3(要排序好的)
if (it != sum.end()) {//存在*it>=left_sum的情况
ans = min(ans, *it - left_sum);
}
if (it != sum.begin()) {//存在*it<left_sum的情况
--it;//left_sum不在sum里面
ans = min(ans, left_sum - *it);
}
}
}
cout << ans;
return 0;
}
INT_MAX
/INT_MIN
:int 类型的最大/最小值(通常为 ±2^31-1)。LONG_MAX
/LONG_MIN
:long 类型的最大/最小值(32位系统同 int,64位系统为 ±2^63-1)。LLONG_MAX
/LLONG_MIN
:long long 类型的最大/最小值(±2^63-1)
1. 数组初始化全为0
方法1:声明时初始化
适用于编译时已知大小的情况,使用{}或{0}初始化:
int arr[n] = {}; // C++11起支持空花括号初始化全0
int arr[n] = {0}; // 显式指定第一个元素为0,其余自动补0
方法2:使用memset
适用于动态或静态数组,需包含<cstring>:
int arr[n];
memset(arr, 0, sizeof(arr)); // 按字节填充0
2. std::vector初始化全为0
方法1:构造函数指定大小和初始值
直接创建含n个0的vector:
std::vector<int> vec(n, 0); // n个元素,每个值为0
方法4:assign重置为0
已存在的vector清空并重新填充:
std::vector<int> vec;
vec.assign(n, 0); // 清空并填充n个0
1. 数字(基本类型)
无添加/删除操作,但可以修改值:
int num = 10;
num = 20; // 修改值
2. 字符串(std::string)
添加元素:
std::string s = "hello";
s.push_back('!'); // 末尾添加字符 → "hello!"
s.insert(2, "xx"); // 在索引2处插入字符串 → "hexxllo!"
s += " world"; // 拼接字符串 → "hexxllo! world"
删除元素:
s.pop_back(); // 删除末尾字符 → "hexxllo! worl"
s.erase(2, 3); // 从索引2开始删除3个字符 → "helo! worl"
3. 数组(静态数组)
静态数组大小固定,需手动模拟操作:
int arr[10] = {1, 2, 3};
int size = 3;
// 模拟末尾添加(需确保不越界)
arr[size++] = 4; // 添加后数组 → {1, 2, 3, 4}
// 模拟删除索引1的元素
for (int i = 1; i < size-1; i++)
arr[i] = arr[i+1]; // 元素前移 → {1, 3, 4}
size--;
4. 动态数组(std::vector)
添加元素:
std::vector<int> v = {1, 2};
v.push_back(3); // 末尾插入 → {1, 2, 3}
v.emplace_back(4); // 高效构造并插入 → {1, 2, 3, 4}
v.insert(v.begin()+1, 5); // 在索引1处插入 → {1, 5, 2, 3, 4}
删除元素:
v.pop_back(); // 删除末尾 → {1, 5, 2, 3}
v.erase(v.begin()+1); // 删除索引1处元素 → {1, 2, 3}
5. 哈希表(std::set / std::map)
std::set 操作:
std::set<int> s = {1, 2};
s.insert(3); // 插入元素 → {1, 2, 3}
s.erase(2); // 删除元素 → {1, 3}
std::map 操作:
std::map<std::string, int> m;
m["a"] = 1; // 插入键值对 → {"a": 1}
m.insert({"b", 2}); // 插入 → {"a": 1, "b": 2}
m.erase("a"); // 删除键"a" → {"b": 2}
6. 栈(std::stack)
添加/删除:
std::stack<int> st;
st.push(1); // 入栈 → [1]
st.push(2); // 入栈 → [1, 2]
st.pop(); // 出栈 → [1](无返回值)
7. 队列(std::queue)
添加/删除:
std::queue<int> q;
q.push(1); // 入队 → [1]
q.push(2); // 入队 → [1, 2]
q.pop(); // 出队 → [2](无返回值)
2023年真题
日期统计
cpp
#include <iostream>
using namespace std;
int num[100]={5,6,8,6,9,1,6,1,2,4,9,1,9,8,2,3,6,4,7,7,5,9,5,0,3,8,7,5,8,1,5,8,6,1,8,3,0,3,7,9,2,
7,0,5,8,8,5,7,0,9,9,1,9,4,4,6,8,6,3,3,8,5,1,6,3,4,6,7,0,7,8,2,7,6,8,9,5,6,5,6,1,4,0,1,
0,0,9,4,8,0,9,1,2,8,5,0,2,5,3,3};
int day[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int ans=0;
int main()
{
for(int m=1;m<=12;m++){
for(int d=1;d<=day[m];d++){
int r[8]={2,0,2,3,m/10,m%10,d/10,d%10};
int k=0;
for(int i=0;i<100;i++){//数字可以重复使用
if(num[i]==r[k]){
k++;
}
if(k==8){
ans++;
break;
}
}
}
}
cout<<ans;
return 0;
}
01串的熵
cpp
#include<bits/stdc++.h>
using namespace std;
//就是枚举0的个数,我还傻乎乎的去简化。。。
int main(){
double n=23333333;//n 必须用 double,否则整数除法会导致 p0 和 p1 计算错误,进而使熵的计算结果完全偏离正确值
double res=11625907.5798;
for(int c0=0;c0<=n/2;c0++){//0 出现次数比 1 少,n-c0>c0
double p0=c0/n;
double p1=(n-c0)/n;
double s=-c0*p0*log2(p0)-(n-c0)*p1*log2(p1);
if(fabs(s-res)<1e-4){
cout<<c0;
break;
}
}
return 0;
}
冶炼金属
cpp
#include <iostream>
using namespace std;
// 数学推导:b<=a/v<b+1 -> a/(b+1)<v<=a/b a/(b+1)取不到,要加1,输出的是整数
const int N=1e4+5;
int main()
{
int n;
cin>>n;
int maxv=1e9;
int minv=1;
for(int i=0;i<n;i++){
int a,b;
cin>>a>>b;
maxv=min(maxv,a/b);
minv=max(minv,a/(b+1)+1);
}
cout<<minv<<" "<<maxv;
return 0;
}
飞机降落
cpp
#include <bits/stdc++.h>
using namespace std;
struct x{
int t,d,l;
};
bool vis[11];
int n;
x plan[11];
bool flag;
void dfs(int cnt,int last)//降落了几架飞机,已降落飞机所用的总时间
{
if(cnt==n){
flag=true;
return ;
}
for(int i=0;i<n;i++){
if(!vis[i]&&plan[i].t+plan[i].d>=last){
vis[i]=true;
dfs(cnt+1,max(plan[i].t,last)+plan[i].l);
vis[i]=false;
}
}
}
int main()
{
int T;
cin>>T;
while(T--){
flag=false;
memset(vis,0,sizeof(vis));
cin>>n;
for(int i=0;i<n;i++){
cin>>plan[i].t>>plan[i].d>>plan[i].l;
}
dfs(0,0);
if(flag)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
接龙数列 (思路大于一切)
cpp
#include <iostream>
using namespace std;
//dp[b]=max(dp[a]+1,dp[b]),dp[b]指的是当前以b为结尾的最长接龙数列
int dp[10];//数字0-9
int main()
{
int n;
cin>>n;
string s;
int m;
for(int i=0;i<n;i++){
cin>>s;
int a=s[0]-'0',b=s[s.size()-1]-'0';
dp[b]=max(dp[a]+1,dp[b]);
m=max(m,dp[b]);
}
cout<<n-m;
return 0;
}
岛屿个数
cpp
#include<bits/stdc++.h>
using namespace std;
int m, n;
char mp[55][55];//0海水1未访问陆地2外海3访问的陆地
//只能用char,不能用int,不然输入的时候都出错了,一行当成一个数
int dirs[8][2] = { -1,0,-1,1,0,1,1,1,1,0,1,-1,0,-1,-1,-1 };
int dirl[4][2] = { -1,0,0,1,1,0,0,-1 };
void out_sea(int x, int y) {//外海染色
if (x<0 || x>m + 1 || y<0 || y>n + 1 || mp[x][y] != '0')return;
mp[x][y] = '2';
for (int i = 0; i < 8; i++) {
out_sea(x + dirs[i][0], y + dirs[i][1]);
}
}
bool is_bast_out_land(int x, int y) {//判断是不是最外围的岛
if (mp[x][y] != '1')return false;
mp[x][y] = '3';
bool flag = false;
//判断当前格子是不是联通外海
for (int i = 0; i < 4; i++) {
int nx = x + dirl[i][0], ny = y + dirl[i][1];
if (nx >= 0 && nx <= m + 1 && ny >= 0 && ny <= n + 1 && mp[nx][ny] == '2') {
flag = true;
}
}
// 继续搜索连通区域
for (int i = 0; i < 4; i++) {
int nx = x + dirl[i][0], ny = y + dirl[i][1];
if (nx > 0 && nx <= m && ny > 0 && ny <= n && mp[nx][ny] == '1') {
flag |= is_bast_out_land(nx, ny);
}
}
return flag;
}
int main() {
int t;
cin >> t;
while (t--) {
memset(mp, '0', sizeof(mp));
cin >> m >> n;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
cin >> mp[i][j];
}
}
out_sea(0, 0);
int ans = 0;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (mp[i][j] == '1' && is_bast_out_land(i, j))ans++;
}
}
cout << ans<<endl;
}
return 0;
}
子串简写
cpp
#include <bits/stdc++.h>
using namespace std;
//当遇到c1时增加cnt计数,当遇到c2时,将当前cnt值累加到答案中
//这保证了每个c2会与之前所有满足距离条件的c1配对,每个c2前面有多少个c1
int k;
char c1, c2;
string s;
long long ans = 0;
int main()
{
cin >> k >> s >> c1 >> c2;
int len = s.size();
long long cnt = 0;
for (int i = 0, j = k - 1; j < len; i++, j++) {
if (s[i] == c1)cnt++;
if (s[j] == c2)ans += cnt;
}
cout << ans;
return 0;
}
整数删除
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
const int K=5e5+5,N=5e5+5;
int n,k;
int l[N],r[N];
ll num[N];
priority_queue<pll,vector<pll>,greater<pll>> p;
int main(){
cin>>n>>k;
l[n+1]=n;
r[0]=1;
for(int i=1;i<=n;i++){
cin>>num[i];
l[i]=i-1;
r[i]=i+1;
p.push({num[i],i});
}
while(k--){
auto [val,ind]=p.top();//学一学写法
if(val!=num[ind]){
p.pop();
k++;
continue;
}
p.pop();
int left=l[ind],right=r[ind];
if(left>=1){
num[left]+=val;
p.push({num[left],left});
}
if(right<=n){
num[right]+=val;
p.push({num[right],right});
}
l[right]=left;
r[left]=right;
}
int sta=r[0];
while(sta<=n){
cout<<num[sta]<<" ";
sta=r[sta];
}
return 0;
}
后面两道都不要了
Day01 BFS基础
99. 岛屿数量(广搜版)
cpp
#include<bits/stdc++.h>
using namespace std;
int dir[4][2]={0,1,1,0,-1,0,0,-1};
int res=0;
void bfs(vector<vector<int>>& map,vector<vector<bool>>& visited,int x,int y){
queue<pair<int,int>> que;//全局队列导致的状态污染:之前把que定义成全局变量会导致多次调用bfs时队列残留前一次的数据。正确做法是在bfs函数内部声明队列,确保每次调用时队列是空的。
que.push({x,y});
visited[x][y]=true;
while(!que.empty()){
pair<int,int> cur=que.front();que.pop();
int curx=cur.first;
int cury=cur.second;
for(int i=0;i<4;i++){
int nextx=curx+dir[i][0];
int nexty=cury+dir[i][1];
if(nextx<0||nextx>=map.size()||nexty<0||nexty>=map[0].size())continue;//这边的xy坐标和坐标系中的是反过来的,x代表第几行,y代表第几列
if(!visited[nextx][nexty]&&map[nextx][nexty]==1){
que.push({nextx,nexty});
visited[nextx][nexty]=true;
}
}
}
}
int main(){
int n,m;
cin>>n>>m;
vector<vector<int>> map(n,vector<int>(m,0));
vector<vector<bool>> visited(n,vector<bool>(m,false));//二维vector初始化格式。初始化的时候可以不像二维数组那样看n,m的大小,直接用n,m
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>map[i][j];
}
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(!visited[i][j]&&map[i][j]==1){
res++;
bfs(map,visited,i,j);
}
}
}
cout<<res;
return 0;
}
洛谷P1443-马的遍历(BFS模板)
cpp
#include<bits/stdc++.h>
using namespace std;
int dir[8][2] = { -2,-1,-2,1,2,-1,2,1,-1,2,1,2,-1,-2,1,-2 };
void bfs(vector<vector<int>>& map, vector<vector<bool>>& visited, int x, int y) {
queue<pair<int, int>> que;
que.push({ x,y });
visited[x][y] = true;
map[x][y] = 0;
while (!que.empty()) {
pair<int, int> cur = que.front();
que.pop();
int curx = cur.first;
int cury = cur.second;
for (int i = 0; i < 8; i++) {
int nextx = curx + dir[i][0];
int nexty = cury + dir[i][1];
if (nextx < 0 || nextx >= map.size() || nexty < 0 || nexty >= map[0].size())continue;
if (visited[nextx][nexty] == false) {
que.push({ nextx,nexty });
visited[nextx][nexty] = true;
map[nextx][nexty] = map[curx][cury]+1;//注意这里加的方式,别用循环一次加加的方式加
}
}
}
}
int main() {
int n, m, x, y;
cin >> n >> m >> x >> y;
vector<vector<int>> map(n, vector<int>(m, -1));
vector<vector<bool>> visited(n, vector<bool>(m, false));
--x;
--y;
bfs(map, visited, x, y);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cout << map[i][j] << " ";
}
cout << endl;
}
}
2021省赛-迷宫(BFS+状态记录)
cpp
#include<bits/stdc++.h>
using namespace std;
char mp[31][51];
bool visited[31][51];
char pri[31][51];//上一个的运动轨迹
int dir[4][2]={{1,0},{0,-1},{0,1},{-1,0}};
char word[4]={'D','L','R','U'};
void print_path(int x,int y){
if(x==0&&y==0){
return;
}
if(pri[x][y]=='D')print_path(x-1,y);
if(pri[x][y]=='L')print_path(x,y+1);
if(pri[x][y]=='R')print_path(x,y-1);
if(pri[x][y]=='U')print_path(x+1,y);
cout<<pri[x][y];
}
void bfs(){
queue<pair<int,int>> que;
que.push({0,0});
visited[0][0]=true;
while(!que.empty()){
pair<int,int> cur=que.front();
que.pop();
int curx=cur.first;
int cury=cur.second;
if(curx==29&&cury==49){
print_path(29,49);
return;
}
for(int i=0;i<4;i++){
int nextx=curx+dir[i][0];
int nexty=cury+dir[i][1];
if(nextx<0||nextx>=30||nexty<0||nexty>=50)continue;
if(visited[nextx][nexty]==false&&mp[nextx][nexty]!='1'){
que.push({nextx,nexty});
visited[nextx][nexty]=true;
pri[nextx][nexty]=word[i];
}
}
}
}
int main(){
for(int i=0;i<30;i++){
cin>>mp[i];
}
bfs();
return 0;
}
Day2 BFS优化
LeetCode 1654. 到家的最少跳跃次数(好多没理解,巨难)
cpp
class Solution {
public:
int minimumJumps(vector<int>& forbidden, int a, int b, int x) {
//a=b,如果一直前进没到达x那就永远到不了了,上限x;
//a>b,任意两步都是往前走的,这时候往回退只能往回退一次不能像a<b的时候以a-b的距离往回退,所以上限是x+b
//a<b,关于a < b的上限一种理解思路:因为a < b,存在一个正整数k,使得(k - 1)a < b ≤ ka,所以当我从原点向前走了k步之后就选择后退一步,就可以确保只后退一步且没有退到负数点上,这个时候的上限就是max(ka,x)。可由于有forbidden的存在,当我们向前走k步之后,往后退一步可能会退forbidden的点上,为了不去考虑诸多情况,我们直接将起点设为f,也就是max(forbidden),这样,从f开始往前走k步之后,后退的一步一定不会落在forbidden的点上,这个时候上限就是max(f + ka,x)。,而因为(k - 1)a < b ≤ ka,所以ka - b < a,即ka < a + b,所以上限可以替换k,得到max(f + a + b,x)。
//综上max(f+a,x)+b;
unordered_set<int> forbiddenSet(forbidden.begin(),forbidden.end());
queue<tuple<int,int,int>> que;
unordered_set<int> visited;
que.push({0,1,0});
visited.emplace(0);
int max_palce=max(*max_element(forbidden.begin(),forbidden.end())+a,x)+b;
while(!que.empty()){
auto[index,direction,step] =que.front();
que.pop();
if(index==x){
return step;
}
int nextindex=index+a;
int nextdirection=1;
if(nextindex>=0&&nextindex<=max_palce&&!visited.count(nextindex * nextdirection)&&!forbiddenSet.count(nextindex)){
que.push({nextindex,nextdirection,step+1});
visited.emplace(nextindex * nextdirection);
}
if(direction==1){//说明之前是前进的,这样就不会出现连续两次后退了
int nextindex=index-b;
int nextdirection=-1;
if(nextindex>=0&&nextindex<=max_palce&&!visited.count(nextindex * nextdirection)&&!forbiddenSet.count(nextindex)){
que.push({nextindex,nextdirection,step+1});
visited.emplace(nextindex * nextdirection);
}
}
}
return -1;
}
};
问题.a<b的上限?visited为什么不能设vector?在设置已经访问过的时候为什么是nextindex * nextdirection而不是nextindex?
洛谷P1135-奇怪的电梯(双向BFS)
cpp
#include<bits/stdc++.h>
using namespace std;
int main() {
int n, a, b;
cin >> n >> a >> b;
int num[201];
for (int i = 0; i < n; i++) {
cin >> num[i];
}
bool visited[201] = { false };
queue<pair<int, int>> que;
que.push({ a,0 });
visited[a] = true;
while (!que.empty()) {
auto [index, step] = que.front();
que.pop();
if (index == b) {
cout<< step;
return 0;
}
int nextindex = index + num[index-1];
if (nextindex >= 1 && nextindex <= n && !visited[nextindex]) {
que.push({ nextindex,step + 1 });
visited[nextindex] = true;
}
nextindex = index - num[index-1];
if (nextindex >= 1 && nextindex <= n && !visited[nextindex]) {
que.push({ nextindex,step + 1 });
visited[nextindex] = true;
}
}
cout << -1;
return 0;
}
力扣 127单词接龙
cpp
class Solution {
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
queue<string> que;
que.push(beginWord);
unordered_map<string,int> visited;
unordered_set<string> wordset(wordList.begin(),wordList.end());
if (wordset.find(endWord) == wordset.end()) return 0;
visited.insert({beginWord,1});
while(!que.empty()){
string word=que.front();
int path=visited[word];
que.pop();
for(int i=0;i<word.size();i++){
string newword=word;
for(int j=0;j<26;j++){
newword[i]=j+'a';
if(newword==endWord){
return path+1;
}
if(wordset.find(newword)!=wordset.end()&&visited.find(newword)==visited.end()){
que.push(newword);
visited.insert({newword,path+1});
}
}
}
}
return 0;
}
};
Day3DFS剪枝
DFS模板:卡码网98. 所有可达路径
邻接矩阵:
cpp
#include<bits/stdc++.h>
using namespace std;
vector<vector<int>> result; // 保存符合条件的所有路径
vector<int> path; // 起点到终点的路径
void dfs(vector<vector<int>> graph,int x,int n){
if(x==n){
result.push_back(path);
return ;
}
for(int i=1;i<=n;i++){
if(graph[x][i]==1){
path.push_back(i);
dfs(graph,i,n);
path.pop_back();
}
}
}
int main(){
int n,m;
cin>>n>>m;
vector<vector<int>> graph(n+1,vector<int>(n+1,0));
int s,t;
for(int i=0;i<m;i++){//循环的是边不是节点数
cin>>s>>t;
graph[s][t]=1;
}
path.push_back(1);
dfs(graph,1,n);
if(result.size()==0){
cout<<-1<<endl;
}else{
for(int i=0;i<result.size();i++){
for(int j=0;j<result[i].size()-1;j++){
cout<<result[i][j]<<" ";
}
cout<<result[i][result[i].size()-1]<<endl;
}
}
return 0;
}
邻接表:
cpp
#include <iostream>
#include <vector>
#include <list>
using namespace std;
vector<vector<int>> result; // 收集符合条件的路径
vector<int> path; // 1节点到终点的路径
void dfs (const vector<list<int>>& graph, int x, int n) {
if (x == n) { // 找到符合条件的一条路径
result.push_back(path);
return;
}
for (int i : graph[x]) { // 找到 x指向的节点
path.push_back(i); // 遍历到的节点加入到路径中来
dfs(graph, i, n); // 进入下一层递归
path.pop_back(); // 回溯,撤销本节点
}
}
int main() {
int n, m, s, t;
cin >> n >> m;
// 节点编号从1到n,所以申请 n+1 这么大的数组
vector<list<int>> graph(n + 1); // 邻接表
while (m--) {
cin >> s >> t;
// 使用邻接表 ,表示 s -> t 是相连的
graph[s].push_back(t);
}
path.push_back(1); // 无论什么路径已经是从0节点出发
dfs(graph, 1, n); // 开始遍历
// 输出结果
if (result.size() == 0) cout << -1 << endl;
for (const vector<int> &pa : result) {
for (int i = 0; i < pa.size() - 1; i++) {
cout << pa[i] << " ";
}
cout << pa[pa.size() - 1] << endl;
}
}
99. 岛屿数量(深搜版)
cpp
#include<bits/stdc++.h>
using namespace std;
int dir[4][2]={1,0,0,1,-1,0,0,-1};
void dfs(int x,int y,vector<vector<int>>& graph,vector<vector<bool>>& visited){
for(int i=0;i<4;i++){
int nextx=x+dir[i][0];
int nexty=y+dir[i][1];
if(nextx<0||nextx>=graph.size()||nexty<0||nexty>=graph[0].size()||graph[nextx][nexty]==0)continue;
if(!visited[nextx][nexty]&&graph[nextx][nexty]==1){
visited[nextx][nexty]=true;
dfs(nextx,nexty,graph,visited);
}
}
}
int main(){
int n,m;
cin>>n>>m;
vector<vector<int>> graph(n,vector<int>(m,0));vector<vector<bool>> visited(n,vector<bool>(m,false));
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>graph[i][j];
}
}
int res=0;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(!visited[i][j]&&graph[i][j]==1){
visited[i][j]=true;
dfs(i,j,graph,visited);
res++;
}
}
}
cout<<res;
return 0;
}
洛谷P1219-八皇后(回溯模板)
cpp
#include<bits/stdc++.h>
using namespace std;
int n;
int ans[14], check[3][26] = { 0 }, sum = 0;//0是列,1是 / x+y=k1 ,2是\ x-y=k2
void pri() {
if (sum >= 3) {
sum++;
return;
}
else {
for (int i = 1; i <= n; i++) {
cout << ans[i] << " ";
}
cout << endl;
}
sum++;
}
void dfs(int line) {//行
if (line == n+1) {
pri();
return;
}
else {
for (int i = 1; i <= n; i++) {//列
if (!(check[0][i]) && !(check[1][line + i]) && !(check[2][line - i + n])) {
check[0][i] = 1;
check[1][line + i] = 1;
check[2][line - i + n] = 1;
ans[line] = i;
dfs(line + 1);
check[0][i] = 0;
check[1][line + i] = 0;
check[2][line - i + n] = 0;
}
}
}
}
int main() {
cin >> n;
dfs(1);
cout << sum;
return 0;
}
2024省赛-数字接龙(DFS)
cpp
#include<bits/stdc++.h>
using namespace std;
int n,k;
int num[11][11];
string path;
bool visited[11][11];//每个格子恰好都经过一次
int dir[8][2]={-1,0,-1,1,0,1,1,1,1,0,1,-1,0,-1,-1,-1};//水平/垂直/对角线方向移动
bool edge[11][11][11][11]; // 检查路径是否交叉,这里edge[a][b][c][d]和edge[c][d][a][b]是两种,而且只有奇数方向的移动才有可能产生交叉
bool dfs(int x,int y){
if(x==n-1&&y==n-1){
return path.size()==n*n-1;
}
for(int i=0;i<8;i++){
int nextx=x+dir[i][0];
int nexty=y+dir[i][1];
if(nextx<0||nextx>n-1||nexty<0||nexty>n-1||
num[nextx][nexty]!=(num[x][y]+1)%k||visited[nextx][nexty]){//判断满足序列顺序最后%的是k,%k-1
//序列里面就没有k-1了
continue;
}
if(i%2==1&&(edge[x][nexty][nextx][y]||edge[nextx][y][x][nexty])){
continue;
}
visited[nextx][nexty]=true;
edge[x][y][nextx][nexty]=true;
path += i + '0'; // 将方向编号加入路径
if(dfs(nextx,nexty)) return true;
path.pop_back();
visited[nextx][nexty]=false;
edge[x][y][nextx][nexty]=false;
}
return false;
}
int main(){
cin>>n>>k;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>num[i][j];
}
}
visited[0][0]=true;
if(!dfs(0,0)){
cout<<-1;
}else{
cout<<path;
}
return 0;
}
//自己写和答案的区别:输出的路径定义,dfs的返回类型,没有路径的处理,检查是否交叉,输出的path长度为n*n-1因为0不计入长度,
//走的时候就是按方向从小到大走的,所以第一次到达终点就是最小的路径
Day4 DFS优化
1. ★2022省赛-修剪灌木(数学推导+DFS优化)
cpp
#include <iostream>
using namespace std;
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++){
cout<<max(i*2,(n-i-1)*2)<<endl;
}
return 0;
}
2. 模拟题-买瓜(贪心剪枝)
cpp
#include <bits/stdc++.h>
using namespace std;
int n,m,ans=1e9;
double sum[35],suf[35];
bool cmp(const double& p,const double& q){
return p>q;
}
void dfs(int index,int cut,double tol){
if(tol>m||tol+suf[index]<m)return ;
if(tol==m){
ans=min(ans,cut);//可能存在多个解
return ;
}
if(index>n)return ;
dfs(index+1,cut,tol+sum[index]);
dfs(index+1,cut+1,tol+sum[index]/2);
dfs(index+1,cut,tol);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>sum[i];
}
sort(sum+1,sum+n+1,cmp);
for(int i=n;i>0;i--){
suf[i]=suf[i+1]+sum[i];
}
dfs(1,0,0);
cout << (ans == 1e9 ? -1 : ans);
return 0;
}
//不能用返回类型为bool类型的dfs:dfs函数的返回类型为void,使其不提前返回。
// 当找到可行解时,更新ans但不终止搜索。这样,即使已经找到一个解,仍然会继续检查其他路径是否有更优解(即更小的cut次数)
Day5 动态规划基础
2021省赛-砝码称重(分组背包DP)
cpp
#include <bits/stdc++.h>
using namespace std;
//假设有a>b两个砝码能称出的重量只有a+b,a-b,b(b一定要用上),这里dp[i(这里的i是0-n)][0]都是1,不然结果都是0,至于
//逻辑上也好理解,无论有没有砝码都能称出0重量
int n,sum=0,ans=0;
int wight[101];
bool dp[101][100001];//bool类型相加会转换成0 1
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>wight[i];
sum+=wight[i];
}
dp[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=sum;j++){
dp[i][j]=dp[i-1][j]+dp[i-1][abs(j-wight[i])]+dp[i-1][j+wight[i]];
}
}
for(int i=1;i<=sum;i++){
if(dp[n][i]){
ans++;
}
}
cout<<ans;
return 0;
}
01背包:蓝桥「ALGO-48 采药」
递推公式多看看理解理解

cpp
#include <iostream>
using namespace std;
int main()
{
int t,m;
int time[101];
int value[101];
cin>>t>>m;
int dp[1001]={0};//记得初始化
for(int i=1;i<=m;i++){
cin>>time[i]>>value[i];
for(int j=t;j>=time[i];--j){
dp[j]=max(dp[j],dp[j-time[i]]+value[i]);
}
}
cout<<dp[t];
return 0;
}
LeetCode 70-爬楼梯(线性DP)
cpp
class Solution {
public:
int climbStairs(int n) {
int dp[46];
dp[1]=1;
dp[2]=2;
if(n==1)return 1;
if(n==2)return 2;
for(int i=3;i<=n;i++){
dp[i]=dp[i-2]+dp[i-1];
}
return dp[n];
}
};
2020国赛-本质上升序列
cpp
#include <iostream>
using namespace std;
//在原字符串去重不对,比如bab:b,a,ab去重后ba:a,b会少;
int main()
{
string s="tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhfiadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqijgihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmadvrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl";
int dp[201];
for(int i=0;i<200;i++){
dp[i]=1;
for(int j=0;j<i;j++){
if(s[j]<s[i]){
dp[i]+=dp[j];
}
if(s[j]==s[i]){
dp[i]-=dp[j];
}
}
}
int ans=0;
for(int i=0;i<200;i++){
ans+=dp[i];
}
cout<<ans;
return 0;
}
Day6 线性DP
2020省赛-数字三角形(二维DP)
cpp
#include <bits/stdc++.h>
using namespace std;
int main()
{
int N;
cin >> N;
int num[101][101];
for (int i = 1; i <=N; i++) {
for (int j = 1; j <= i; j++) {
cin >> num[i][j];
}
}
int dp[101][101];
dp[1][1] = num[1][1];
for (int i = 2; i <=N; i++) {
for (int j = 1; j <= i; j++) {
if (j == 1)dp[i][j] = num[i][j] + dp[i - 1][j];
else if (j == i)dp[i][j] = num[i][j] + dp[i - 1][j - 1];
else dp[i][j] = num[i][j] + max(dp[i - 1][j], dp[i - 1][j - 1]);
}
}
if (N % 2 == 1) {
cout << dp[N][N / 2 + 1];
}
else {
cout << max(dp[N][N / 2], dp[N][N / 2 + 1]);
}
return 0;
}
力扣 300最长递增子序列(LIS)
cpp
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> dp(nums.size(), 1);
int ans=0;
for(int i=0;i<nums.size();i++){
for(int j=0;j<i;j++){
if(nums[j]<nums[i]){
dp[i]=max(dp[i],dp[j]+1);
}
}
ans=max(ans,dp[i]);
}
return ans;
}
};
洛谷P1091-合唱队形(LIS变形)
cpp
#include<bits/stdc++.h>
using namespace std;
int n, ans;
int t[101];
int dp[2][101];
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> t[i];
dp[0][i] = 1;
dp[1][i] = 1;
for (int j = 0; j < i; j++) {
if (t[j] < t[i]) {
dp[0][i] = max(dp[0][i], dp[0][j] + 1);
}
}
}
for (int i = n - 1; i >= 0; i--) {
for (int j = n - 1; j > i; j--) {
if (t[j] < t[i]) {
dp[1][i] = max(dp[1][i], dp[1][j] + 1);
}
}
}
for (int i = 0; i < n; i++) {
ans = max(ans, dp[0][i] + dp[1][i]);
}
cout << n - (ans - 1);
return 0;
}
Day7 树形DP
洛谷P1352-没有上司的舞会(模板)
cpp
#include<bits/stdc++.h>
using namespace std;
int n;
int hap[6001];
vector<int> son[6001];
int vis[6001];
int root;
int f[6001][2];
void dfs(int x) {
f[x][0] = 0;//不选当前节点,初始值是0
f[x][1] = hap[x];//选当前节点,初始值是1
for (int i = 0; i < son[x].size(); i++) {
int y = son[x][i];//x的子节点
dfs(y);
f[x][0]+= max(f[y][0], f[y][1]);//不选当前节点,那么最大happy取决与子节点取不取
f[x][1] += f[y][0];//选当前节点,那么子节点肯定是不取的
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> hap[i];
}
for (int i = 1; i <= n - 1;i++) {//n-1=2n-(n+2)+1
int s, f;
cin >> s >> f;
son[f].push_back(s);
vis[s] = 1;
}
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
root = i;
break;
}
}
dfs(root);
cout << max(f[root][0], f[root][1]);
return 0;
}
不规则的多维数组 ,用vector push_back

LeetCode 337-打家劫舍III(状态机)(思路跟上面那个一样,但是格式写不出来。。。还有auto)
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
pair<int,int> dfs(TreeNode* node){
if(node==nullptr){
return {0,0};
}
auto[l_r,l_n_r]=dfs(node->left);
auto[r_r,r_n_r]=dfs(node->right);
int n_r=l_n_r+r_n_r+node->val;//偷当前节点,自己的值,但是左右子节点不能偷
int n_n_r=max(l_r,l_n_r)+max(r_r,r_n_r);//不偷当前节点,没有自己的值,左右子节点可偷可不偷
return {n_r,n_n_r};
}
public:
int rob(TreeNode* root) {
auto[root_rob,root_not_rob]=dfs(root);
return max(root_rob,root_not_rob);
}
};
2018省赛- 完全二叉树的权值(结构处理)
cpp
#include <bits/stdc++.h>
using namespace std;//不是满二叉树
int n;
int val[100001];
long long dep[20];
void dfs(int x,int deep){
if(x>n)return;
dep[deep]+=val[x];
dfs(2*x,deep+1);
dfs(2*x+1,deep+1);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>val[i];
}
memset(dep, 0, sizeof(dep)); // 数组全部初始化为0
dfs(1,1);
int ans=1;
for(int i=2;i<=log2(n)+1;i++){
if(dep[i]>dep[ans]){
ans=i;
}
}
cout<<ans;
return 0;
}
Day8 数位DP(不行就算了)
LeetCode 233-数字1的个数(格式不用理解,主要看dfs)
cpp
class Solution {
private:
string s;
int len;
vector<vector<int>> memo;
int dfs(int i,int cnt1,bool is_limit){
if(i==len)return cnt1;
if(!is_limit&&memo[i][cnt1]!=-1){//memo[i][cnt1]!=-1这个不能去掉
return memo[i][cnt1];
}
int res=0;
int up=is_limit? s[i]-'0':9;
for(int j=0;j<=up;j++){
res+=dfs(i+1,cnt1+(j==1),is_limit&&(j==up));
}
if(!is_limit)memo[i][cnt1]=res;
return res;
}
public:
int countDigitOne(int n) {
s=to_string(n);
len=s.size();
memo.assign(len,vector<int> (len,-1));
return dfs(0,0,true);
}
};
2. 洛谷P2602-数字计数
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll solve(ll n, int target) {
if (n < 0)return 0;
string s = to_string(n);
int len = s.size();
vector<vector<ll>> memo(len, vector<ll>(len, -1));
function<ll(int, ll, bool, bool)> dfs = [&](int i, ll cnt, bool is_limit, bool is_num) {//lambda表达式,在函数里面写函数
if (i == len) {
return is_num ? cnt : (target == 0 ? 1ll : 0ll);//目标0的话,000算一个0
}
if (!is_limit && is_num && memo[i][cnt] != -1) {
return memo[i][cnt];
}
ll res = 0;
int up = is_limit ? s[i] - '0' : 9;
for (int d = 0; d <= up; d++) {
bool newis_num = is_num || (d != 0);//先判断加了d是不是有效数字,在去加cnt,不然000会算3个0
ll newcnt = cnt + (newis_num && d == target);
res += dfs(i + 1, newcnt, is_limit && (d == up), newis_num);
}
if (!is_limit && is_num) {
memo[i][cnt] = res;
}
return res;
};
return dfs(0, 0, true, false);//刚开始从高位往下的时候,会遇到00i之类的,这时候要是i=0,is_num就是false,所以刚开始的时候也是
}
int main() {
ll a, b;//int类型最大是10^10左右
cin >> a >> b;
for (int digit = 0; digit <= 9; ++digit) { // 计算每个数字的出现次数
ll upper = solve(b, digit);
ll lower = solve(a - 1, digit);
cout << upper - lower << " ";
}
return 0;
}
2022省赛-好数(超出能力范围了,算了)
cpp
#include <bits/stdc++.h>
using namespace std;
int cnt[20];
long long ppow[22];
using Pair = pair<long long, long long>; // first代表个数,second代表总和
Pair dp[20][10][10][10][10][2]; // 每个元素默认初始化(0,0)
Pair dfs(int len, int p1, int p2, int p3, int p4, bool ok, bool limit) {
if (len == 0)
return {ok ? 1 : 0, 0};
if (!limit && dp[len][p1][p2][p3][p4][ok].first != 0)
return dp[len][p1][p2][p3][p4][ok];
Pair ans = {0, 0};
int up = limit ? cnt[len] : 9;
for (int i = 0; i <= up; ++i) {
Pair t;
if (p2 == 2 && p3 == 0 && p4 == 2 && i == 2)
t = dfs(len - 1, p2, p3, p4, i, true, limit && (i == up));
else
t = dfs(len - 1, p2, p3, p4, i, ok, limit && (i == up));
ans.first += t.first; // 累加满足条件的个数
ans.second += t.second + 1LL * i * ppow[len - 1] * t.first; // 计算当前位的贡献
}
if (!limit)
dp[len][p1][p2][p3][p4][ok] = ans;
return ans;
}
long long solve(long long x) {
int len = 0;
while (x > 0) {
cnt[++len] = x % 10;
x /= 10;
}
return dfs(len, 0, 0, 0, 0, false, true).second;
}
int main() {
ppow[0] = 1;
for (int i = 1; i < 20; ++i)
ppow[i] = ppow[i - 1] * 10;
int a, b;
cin >> a >> b;
cout << solve(b) - solve(a - 1) << endl;
return 0;
}
Day9 状态压缩DP(不行就算了)
LeetCode 847-最短访问路径(状压+BFS)
cpp
class Solution {
public:
int shortestPathLength(vector<vector<int>>& graph) {
int n=graph.size();
queue<tuple<int,int,int>> que;//index,mask,path长度
vector<vector<bool>> visited(n,vector<bool> (1<<n,false));//第2个下标别写n,(1<<n)-1和n的长度不一样的,会越界,第一个下标代表的是当前节点,如果代表的是起始点那么相同的路线不同方向会被当成不同状态
for(int i=0;i<n;i++){
que.push({i,1<<i,0});
visited[i][1<<i]=true;
}
while(!que.empty()){
auto[index,mask,path] =que.front();
que.pop();
if(mask==(1<<n)-1){
return path;
}
for(int x:graph[index]){
int nextmask=mask|1<<x;
if(!visited[x][nextmask]){
que.push({x,nextmask,path+1});
visited[x][nextmask]=true;
}
}
}
return -1;
}
};
洛谷P1896-互不侵犯(没看懂)
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int N, K;
cin >> N >> K;
const int limit = 1 << N;
// 预处理合法行状态及转移关系
vector<int> valid_states;
for (int st = 0; st < limit; ++st) {
if (st & (st << 1)) continue; // 行内冲突则跳过
valid_states.push_back(st);
}
// dp[row][k][st]:前row行放置k个国王,第row行状态为st的方案数
vector<vector<vector<long long>>> dp(N+1, vector<vector<long long>>(K+1, vector<long long>(limit, 0)));
dp[0][0][0] = 1;
// 预处理合法转移
vector<vector<pair<int, int>>> transitions(limit);
for (int st : valid_states) {
for (int nst : valid_states) {
if ((nst & (st << 1)) || (nst & st) || (nst & (st >> 1))) continue; // 行间冲突
int cnt = __builtin_popcount(nst);
transitions[st].emplace_back(nst, cnt);
}
}
// 状态转移
for (int row = 0; row < N; ++row) {
for (int k = 0; k <= K; ++k) {
for (int st : valid_states) {
if (dp[row][k][st] == 0) continue;
for (auto& [nst, cnt] : transitions[st]) {
if (k + cnt > K) continue;
dp[row + 1][k + cnt][nst] += dp[row][k][st];
}
}
}
}
// 累加最终结果
long long ans = 0;
for (int st : valid_states) ans += dp[N][K][st];
cout << ans << endl;
}
Day10 贪心算法
洛谷P1223-排队接水(好多基础的)
cpp
#include<bits/stdc++.h>
using namespace std;
struct a {
int index, num;
};//定义结构体
bool cmp(a x, a y)
{
return x.num < y.num;
}//定义排序顺序
int main() {
int n;
cin >> n;
struct a time[1001];//结构体数组
for (int i = 1; i <= n; i++) {
cin >> time[i].num;
time[i].index=i;
}
sort(time + 1, time + n + 1, cmp);//快排
for (int i = 1; i <= n; i++) {
cout << time[i].index<<" ";
}
cout << endl;
double ans = 0;
for (int i = n - 1; i >= 0; i--) {
ans += time[n - i].num * i;
}
printf("%.2lf", ans / n);//输出保留两位
return 0;
}
LeetCode 435-无重叠区间(区间贪心)(二维vector输入,没有数量的)
cpp
#include <bits/stdc++.h>
using namespace std;
// 按区间结束时间升序排序
bool cmp(const vector<int>& a, const vector<int>& b) {
return a[1] < b[1];
}
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if (intervals.size() < 2) return 0;
sort(intervals.begin(), intervals.end(), cmp); // 按结束时间排序
int removed = 0;
int prev_end = intervals[0][1];
for (int i = 1; i < intervals.size(); ++i) {
if (intervals[i][0] < prev_end) {
removed++; // 当前区间与前一区间重叠,需移除
}
else {
prev_end = intervals[i][1]; // 无重叠,更新结束时间
}
}
return removed;
}
int main() {
vector<vector<int>> intervals;
int a, b;
while (cin >> a >> b) {
intervals.push_back({ a, b });
}
int result = eraseOverlapIntervals(intervals);
cout << result;
return 0;
}
Day11 数论基础(质数)
质数筛:LeetCode 204 计算质数
cpp
class Solution {
public:
int countPrimes(int n) {
//如果x是质数,那么2*x,3*x.....不会是质数
//对于一个质数x,如果按上文说的我们从2x开始标记其实是冗余的,应该直接从x⋅x开始标记,因为2x,3x,...这些数一定在x之前就被其他数的倍数标记过了,例如2的所有倍数,3的所有倍数等。
vector<int> isPrime(n,1);
int res=0;
for(int i=2;i<n;i++){
if(isPrime[i]==1){
for(long long j=(long long)i*i;j<n;j+=i){
isPrime[j]=0;
}
res++;
}
}
return res;
}
};
洛谷P3383-线性筛模板
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);//加速用的
int n, q;
cin >> n >> q;
vector<int> k(q);
for (int i = 0; i < q; ++i) {
cin >> k[i];
}
// 使用vector<bool>避免栈溢出,并正确初始化
vector<bool> is_prime(n + 1, true);
is_prime[0] = is_prime[1] = false;
for (int x = 2; x * x <= n; ++x) {
if (is_prime[x]) {
for (int j = x * x; j <= n; j += x) {
is_prime[j] = false;
}
}
}
vector<int> primes;
for (int i = 2; i <= n; ++i) {
if (is_prime[i]) {
primes.push_back(i);
}
}
for (int idx : k) {
if (idx - 1 < primes.size()) {
cout << primes[idx - 1] << '\n';
}
}
return 0;
}
Day12 GCD与LCM
LeetCode 914-卡牌分组(GCD应用)
cpp
class Solution {
int cnt[10000];
public:
bool hasGroupsSizeX(vector<int>& deck) {
//1.x不是卡牌里面的数字,所以1<=x<=10000(比如全是1,分成1组,len最大的话x就是10000)
//2.相同的可以不分在一起,比如deck=[1,1,1,1]x=2
//3.结论x是len,cnt(每个数的个数)的约数
for(auto x:deck)cnt[x]++;
int ans=-1;
for(int i=0;i<10000;i++){
if(cnt[i]){
if(~ans){
ans=gcd(ans,cnt[i]);
}else{
ans=cnt[i];
}
}
}
return ans>=2;
}
};
洛谷P1029-最大公约数和最小公倍数问题
cpp
#include <bits/stdc++.h>
using namespace std;
long long x,y,ans=0;
int main() {
//算一半,结果+2的加
cin>>x>>y;
if(x==y) ans--;//一样的话就不算2个
x*=y;//所以要开longlong
for(long long i=1;i<=sqrt(x);i++){//别从0开始,%0会出错
if(x%i==0&&(x/gcd(i,x/i))==y)ans+=2;//没有lcm库函数
}
cout<<ans;
return 0;
}
2022省赛-因数计数(容斥和调和级数枚举,md,原理都不知道,写个蛋)
cpp
#include <iostream>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int cnt[N]; // 记录每个数出现的次数
int fac[N]; // 记录每个数的因数(不含自身)出现的次数
int mul[N]; // 记录每个数的倍数(不含自身)出现的次数
signed main() {
int n;
cin >> n;
int x;
for (int i = 0; i < n; i++) {
cin >> x;
cnt[x]++; // 统计输入数值出现次数
}
int ans = 0;
for (int i = 1; i <= N; i++) {
// 初始化当前数的因数和倍数统计(排除自身)
fac[i] += cnt[i] - 1; // 因数统计(i的因数包含自己,但需排除)
mul[i] += cnt[i] - 1; // 倍数统计(i的倍数包含自己,但需排除)
// 调和级数枚举:统计所有因数和倍数
for (int j = 2 * i; j <= N; j += i) {
fac[j] += cnt[i]; // j的因数i出现的次数累加
mul[i] += cnt[j]; // i的倍数j出现的次数累加
}
ans += cnt[i] * mul[i]; // 初步统计有效数对
}
ans *= ans; // 平方得到所有数对组合(含重复和无效)
// 容斥修正:减去重复和无效组合
for (int i = 1; i <= N; i++) {
ans -= cnt[i] * mul[i] * mul[i]; // 重复贡献:i作为因数的多次组合
ans -= cnt[i] * fac[i] * fac[i]; // 无效组合:i既不是因数也不是倍数
ans -= cnt[i] * mul[i] * fac[i] * 2; // 交叉重复:因数与倍数的交叉组合
ans += cnt[i] * mul[i]; // 修正误减项
ans += cnt[i] * (cnt[i] - 1); // 同一数值的数对(如i,i)
}
cout << ans << "\n";
return 0;
}
2024省赛-宝石组合(数学分析)
cpp
#include <bits/stdc++.h>
using namespace std;
//化简之后s=gcd(ha,hb,hc),与其遍历三个h不如遍历s,s要大,从最大开始遍历,因为h最大时1e5,所以s也是
//gcd是最大公约数,所以ha=x*s,hb=y*s,hc=z*s
int n;
int h[100001];
int cnt[100001]={0};
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>h[i];
cnt[h[i]]++;
}
sort(h+1,h+n+1);
for(int s=100000;s>=1;s--){
int ans[3];//答案
int tol=0,sum=0;//找到了几个宝石,放进答案里面几个石头
for(int j=s;j<=100000;j+=s){//s的倍数
tol+=cnt[j];//下面的代码都是为了防止有相同的闪亮度
for(int i=0;i<cnt[j]&&sum<3;i++){//i代表j闪亮度的数量,起到作用是展示循环次数,但是下标
//是sum,因为比如i=1,ans[0]=j,下一个cnt[j]也是1,这时下标要从2开始了
ans[sum]=j;
sum++;
}
if(tol>=3){//有可能一下子会有很多
cout<<ans[0]<<" "<<ans[1]<<" "<<ans[2];
return 0;//找到了就直接return,不然会输出好多
}
}
}
}
Day13 快速幂与矩阵运算与高精度
1. 洛谷P1226-快速幂模板
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b,p;
cin>>a>>b>>p;
int z=b;
long long ans=1,base=a;
while(b>0){
if(b&1){
ans*=base;
ans%=p;
}
base*=base;
base%=p;
b>>=1;
}
cout<<a<<"^"<<z<<" "<<"mod"<<" "<<p<<"="<<ans;
return 0;
}
LeetCode 50-Pow(x,n)(指数为负数的情况)
cpp
class Solution {
public:
double myPow(double x, int n) {//-231 <= n <= 231-1,如果是int n=-231,取反之后会超出int范围
long double ans=1,base=x;
long long b=n;
if(b<0){
b=-b;
base=1/base;
}
while(b>0){
if(b&1){
ans*=base;
}
base*=base;
b>>=1;
}
return ans;
}
};
2014省赛-斐波那契(矩阵快速幂)(暂且不要)
2024省赛-R格式(快速幂+浮点处理)
cpp
#include <bits/stdc++.h>
using namespace std;//不能用快速幂先计算出2^n,2^1000太大了,long long 装不下
int n,st;
string d;
int num[10000];
int main()
{
cin>>n>>d;
int len=d.size()-1;
int temp=len;//倒叙输入标记用的,为什么不用i因为.不参与num
for(int i=len;i>=0;i--){
if(d[i]=='.'){
st=len-i;
continue;
}
num[len-temp]=d[i]-'0';
temp--;
}
for(int i=1;i<=n;i++){
for(int j=0;j<=len;j++){
num[j]*=2;
}
for(int k=0;k<len+1;k++){
num[k+1]+=num[k]/10;
num[k]=num[k]%10;
}
if(num[len])len++;
}
if(num[st-1]>=5){
num[st]+=1;
for(int i=st+1;i<len;i++){
num[i+1]+=num[i]/10;
num[i]=num[i]%10;
}
if(num[len])len++;
}
for(int i=len-1;i>=st;i--){
cout<<num[i];
}
return 0;
}
Day14并查集
洛谷P3367-并查集模板(内含加速代码)
cpp
#include<bits/stdc++.h>
using namespace std;
int n,m,z,x,y;
int f[200005];
int find(int x){
if(f[x]==x){
return x;
}
f[x]=find(f[x]);
return f[x];
}
int main(){
ios::sync_with_stdio(false); //cin&cout加速
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
f[i]=i;
}
for(int i=1;i<=m;i++){
cin>>z>>x>>y;
int r1=find(x),r2=find(y);
if(z==1){
f[r1]=r2;
}else{
if(r1==r2){
cout<<"Y"<<endl;
}else{
cout<<"N"<<endl;
}
}
}
return 0;
}
LeetCode 684-冗余连接(判断是否成环)
cpp
class Solution {
private:
int f[1005];
int find(int x) {
if (f[x] != x) {
f[x] = find(f[x]); // 路径压缩优化
}
return f[x];
}
public:
vector<int> findRedundantConnection(vector<vector<int>>& edges) {
int n = edges.size();
for (int i = 1; i <= n; i++) f[i] = i; // 初始化
for (auto& edge : edges) {
int u = edge[0], v = edge[1];
int rootU = find(u), rootV = find(v);
if (rootU == rootV) {
return edge; // 发现环,直接返回当前边
} else {
f[rootV] = rootU; // 合并集合
}
}
return {};
}
};
蓝桥杯 连通块中点的数量
cpp
#include <iostream>
using namespace std;
int n,m;
int f[100005],s[100005];
int find(int x){
if(x==f[x]){
return x;
}
f[x]=find(f[x]);
return f[x];
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
f[i]=i;
s[i]=1;
}
string x;
int a,b;
for(int i=1;i<=m;i++){
cin>>x;
if(x=="C"){
cin>>a>>b;
int r1=find(a),r2=find(b);
if(r1!=r2){
f[r2]=r1;
s[r1]+=s[r2];
}
}else if(x=="Q1"){
cin>>a>>b;
cout<<(find(a)==find(b)?"Yes":"No")<<endl;
}else{
cin>>a;
cout<<s[find(a)]<<endl;
}
}
return 0;
}
2020省赛-七段码
cpp
#include <iostream>
using namespace std;
int graph[7][7] = {
{0,1,0,0,0,1,0},
{1,0,1,0,0,0,1},
{0,1,0,1,0,0,1},
{0,0,1,0,1,0,0},
{0,0,0,1,0,1,1},
{1,0,0,0,1,0,1},
{0,1,1,0,1,1,0}
};
bool sel[7]; // 记录选中的二极管
int parent[7];
int find(int x) {
return parent[x] == x ? x : parent[x] = find(parent[x]);
}
bool check() {
for (int i=0; i<7; i++) parent[i] = i;
for (int i=0; i<7; i++)
for (int j=0; j<7; j++)
if (sel[i] && sel[j] && graph[i][j])
parent[find(i)] = find(j);
int root = -1;
for (int i=0; i<7; i++) {
if (!sel[i]) continue;
if (root == -1) root = find(i);
else if (find(i) != root) return false;
}
return root != -1;
}
void dfs(int k, int &ans) {
if (k == 7) {
if (check()) ans++;
return;
}
sel[k] = true;
dfs(k+1, ans);
sel[k] = false;
dfs(k+1, ans);
}
int main() {
int ans = 0;
dfs(0, ans);
cout << ans; // 输出80
return 0;
}
Day15 优先队列(不手写堆)
洛谷P1090-合并果子(堆模板)
cpp
#include<bits/stdc++.h>
using namespace std;
int n,ans=0;
int num[10005];
priority_queue <int,vector<int>,greater<int> >q;
int main(){
cin>>n;
for(int i=0;i<n;i++){
cin>>num[i];
q.push(num[i]);
}
while(q.size()>1){//最后会剩余一个元素
int r1=q.top();q.pop();
int r2=q.top();q.pop();
ans+=r1+r2;
q.push(r1+r2);
}
cout<<ans;
return 0;
}
LeetCode 215-数组第K大元素(堆应用)
cpp
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {//维护一个大小为k的小根堆
priority_queue<int,vector<int>,greater<int> > q;
for(int i=0;i<nums.size();i++){
if(q.size()<k){
q.push(nums[i]);
}
else{
if(nums[i]>q.top()){
q.pop();
q.push(nums[i]);
}
}
}
return q.top();
}
};
Day16 字符串处理
Day17 哈希表应用
洛谷P1102-A-B数对(哈希优化)
cpp
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
ll n, c, ans = 0;
unordered_map<ll, ll> m;
ll num[200005];
int main() {
cin >> n >> c;
for (int i = 0; i < n; i++) {
cin >> num[i];
m[num[i]]++;
}
for (int i = 0; i < n; i++) {
ans += m[num[i] + c];
}
cout << ans;
return 0;
}
LeetCode 1-两数之和(经典题)
cpp
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> m;
for (int i = 0; i < nums.size(); i++) {
int complement = target - nums[i];
// 查找已存储的补数(保证补数不是当前元素)
if (m.find(complement) != m.end()) {
return {m[complement], i};
}
// 当前元素存入哈希表(供后续元素查找)
m[nums[i]] = i;
}
return {};
}
};
蓝桥杯 字符统计
cpp
#include <bits/stdc++.h>
using namespace std;
int main()
{
map<char,int> m;//默认按key值从小到大排序
string s;
cin>>s;
int maxs=0;
for(char i:s){
m[i]++;
maxs=max(maxs,m[i]);
}
for(auto it=m.begin();it!=m.end();it++){
if(it->second==maxs) cout<<it->first;
}
return 0;
}
Day18 图论基础
1. 洛谷P4779-Dijkstra模板(堆优化)
cpp
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N=1e5+1,W=0x3f3f3f3f; // 标准无穷大常量
vector<pii> graph[N];
int dij[N];
bool vis[N];
int n,m,s;
void dijkstra(int start){
memset(dij, 0x3f, sizeof(dij));// 初始化更规范
priority_queue <pii,vector<pii>,greater<pii> > q;
q.push({0,start});//根据距离排的,距离在第一位
dij[start]=0;
while(!q.empty()){
auto t=q.top();
q.pop();
int u1=t.second,v=t.first;
if(vis[u1])continue;
vis[u1]=true;
for(auto edge :graph[u1]){
int u2=edge.first;
int w=edge.second;
if(v+w<dij[u2]){
dij[u2]=v+w;
q.push({dij[u2],u2});
}
}
}
}
int main(){
cin>>n>>m>>s;
int u1,u2,w;
for(int i=1;i<=m;i++){
cin>>u1>>u2>>w;
graph[u1].push_back({u2,w});
}
dijkstra(s);
for(int i=1;i<=n;i++){
if(i!=n){
cout<<dij[i]<<" ";
}else{
cout<<dij[i];
}
}
return 0;
}
2019省赛-最短路径(应用)
cpp
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
vector<pii> graph[20];
int dij[20];
bool vis[20];
void add(char x,char y,int c)
{
int a=x-'A'+1;
int b=y-'A'+1;
graph[a].emplace_back(b,c);
graph[b].emplace_back(a,c);//vector<pair<int,int>> graph初始化如果是无向图需要双向添加
}
void dijkstra(int x){
for(int i=1;i<=19;i++){
dij[i]=100;
}
dij[1]=0;
priority_queue <pii,vector<pii>,greater<pii>>q;
q.push({0,1});
while(!q.empty()){
auto t=q.top();
q.pop();
int v=t.first;
int u1=t.second;
if(vis[u1]) continue;
vis[u1]=true;
for(auto edge: graph[u1]){
int u2=edge.first;
int w=edge.second;
if(v+w<dij[u2]){
dij[u2]=v+w;
q.push({dij[u2],u2});
}
}
}
}
int main()
{
add('A','B',2);
add('A','C',1);
add('A','D',1);
add('A','D',1);
add('B','J',2);
add('B','G',1);
add('C','D',3);
add('C','F',3);
add('C','G',3);
add('D','E',1);
add('D','G',2);
add('D','H',1);
add('D','I',2);
add('E','H',1);
add('E','I',3);
add('F','G',1);
add('F','J',1);
add('G','F',1);
add('G','I',3);
add('G','K',2);
add('H','I',1);
add('H','L',2);
add('I','M',3);
add('J','S',2);
add('K','N',1);
add('K','L',3);
add('K','P',2);
add('L','M',1);
add('L','R',1);
add('M','N',2);
add('M','Q',1);
add('M','S',1);
add('N','P',1);
add('O','P',1);
add('O','Q',1);
add('O','R',3);
add('R','S',1);
dijkstra(1);
cout<<dij['S'-'A'+1];
return 0;
}
蓝桥杯2016-路径计数(邻接矩阵)(dfs)
cpp
#include <bits/stdc++.h>
using namespace std;
// 求路径的数量还是用dfs更好一点,bfs一般用于求最短路径
//正确答案是206
int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
bool visited[6][6];//走的是边不是格子,所以是6*6
int ans=0;
void dfs(int x,int y,int path){
if(path>12)return ;
if(path>2&&x==0&&y==0){//去掉下上,右左这两个自交的路线,筛掉2步重合回到起点的路径
ans++;
return;
}
for(int i=0;i<4;i++){
int curx=x+dir[i][0];
int cury=y+dir[i][1];
if(curx<0||curx>5||cury<0||cury>5||visited[curx][cury])continue;
visited[curx][cury]=true;
dfs(curx,cury,path+1);
visited[curx][cury]=false;
}
return ;
}
int main()
{
dfs(0,0,0);
cout<<ans;
return 0;
}
Day19 最小生成树
洛谷P3366-Kruskal模板
cpp
#include<bits/stdc++.h>
using namespace std;
const int N=5005,M=2*1e5+5;
int n,m;
struct Edge{
int u,v,w;
};
Edge edges[M];
int f[N];
bool cmp(const Edge a,const Edge b){
return a.w<b.w;
}
int find(int x){
return f[x]==x? x:f[x]=find(f[x]);
}
int kruskal(){
for(int i=1;i<=n;i++){
f[i]=i;
}
sort(edges,edges+m,cmp);
int cnt=0;//边
int ans=0;//长度之和
for(int i=0;i<m;i++){
int u=edges[i].u,v=edges[i].v,w=edges[i].w;
int fu=find(u),fv=find(v);
if(fu!=fv){
f[fv]=fu;
ans+=w;
cnt++;
if(cnt==n-1)break;
}
}
return cnt==n-1? ans:-1;
}
int main(){
cin>>n>>m;
for(int i=0;i<m;i++){
cin>>edges[i].u>>edges[i].v>>edges[i].w;
}
int res=kruskal();//不能用三元表达式判断kruskal()输出,因为这样会调用2次kruskal()函数。也不能用cout<<(res==-1? 'orz': res);因为三元表达式要求后面两个数据类型一致
if (res == -1) cout << "orz";
else cout << res;
return 0;
}
dotcpp蓝桥杯历届试题-城市建设(Kruskal变式)
cpp
//1.负权值的路无论是否成环都应该选择,因为能赚钱
//2.为何分两次计算(纯道路 vs 道路+码头)?
// 如果道路联不通,那么只能选择道路加码头,反之选择min(存在仅道路比混合费用更小的情况),
// 至于仅码头在算道路加码头的时候算过了,还有负权值的道路会影响是否选择码头,所以分两次算
//3.为什么用虚拟节点?直接叠加两节点费用是局部最优陷阱
//举例:场景设定 节点A - B道路费用5,A、B、C码头费用分别为3、4、2,且C无道路连接。
//直接叠加逻辑:min(5,3+4)+2+3+4
//虚拟节点:2+3+4
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4+5, M = 1e5+1e4+5;//加了虚拟节点后edges的最大值可能会增加
int n, m;
struct Edge{
int u, v, w;
};
Edge edges[M];
int dock[N];
int f[N];
bool cmp(const Edge a, const Edge b) {
return a.w < b.w;
}
int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
int kruskal(int n, int m) {
for (int i = 0; i <= n; i++) {//虚拟节点是0
f[i] = i;
}
int cnt = 0;
int ans = 0;
sort(edges, edges + m, cmp);
for (int i = 0; i < m; i++) {
int u= edges[i].u,v=edges[i].v,w=edges[i].w;
int fu = find(u), fv = find(v);
if (fu != fv || w < 0) {//负权边不算在最小生成树要求的n-1条边里面,他是必须加的
if (fu != fv) {
f[fv] = fu;
cnt++;
}
ans += w;
}
}
return (cnt >= n - 1) ? ans : 1e9;//有负权边所以是>=
}
int main() {
cin >> n >> m;
for (int i = 0; i < m; i++) {
cin >> edges[i].u >> edges[i].v >> edges[i].w;
}
int node_cnt = m;
for (int i = 1; i <= n; i++) {
cin >> dock[i];
if (dock[i] != -1) {
edges[node_cnt++] = { 0,i,dock[i] };
}
}
int a1=kruskal(n, m);
int a2 = kruskal(n + 1, node_cnt);
if (a1 == 1e9)cout << a2;
else cout << min(a1, a2);
return 0;
}
Day20 拓扑排序
有关数组和vector初始化的注意事项
1
邻接表存储图(如 vector<int> edges[N])
邻接表 edges[N] 的每个元素 edges[i] 都是一个独立的 vector<int>,默认情况下每个 edges[i] 是空容器。因此:直接通过下标访问未分配的空间(如 edges[i][j])会导致越界访问(未定义行为)。必须通过动态添加元素(如 push_back)或预先分配空间(如构造函数指定大小)来保证操作合法。
2
vector<int> a[N]
这是一个静态数组,数组长度为 N,每个元素都是一个 vector<int> 对象。可以理解为二维结构,但第一维(数组维度)是固定大小的,第二维(每个 vector 的容量)是可变的
vector<int> b(N)
这是一个动态数组(单个 vector 对象),初始化时分配了 N 个元素的存储空间,每个元素默认初始化为 0(或通过构造函数指定值)。这是一个一维结构,但可以通过嵌套定义实现多维
特性 vector<int> a[N] vector<int> b(N)
维度 固定一维数组,元素为动态二维vector 单个一维动态vector
内存分配 数组静态分配,元素动态分配 整体动态分配
默认初始化 每个 vector 为空 预分配 N 个元素(默认值 0)
动态扩展能力 仅第二维可动态扩展 整体可动态扩展
典型应用 邻接表、多容器独立管理 一维动态数据存储
3
- int a[N]:声明数组
语法含义:
声明一个数组,数组名为 a,包含 N 个 int 类型的元素。
N 必须是编译期常量(如 const int N = 5; 或 #define N 5)。
- int b(N):初始化变量
语法含义:
声明一个变量 b 并用值 N 初始化它。
示例:
int N = 5;
int b(N); // 合法:声明一个int变量b,初始化为N的值(5)
特性 int a[N] int b(N)
类型 数组声明 变量声明并初始化
N的约束 N 必须是编译期常量(C++标准要求) N 可以是任意int兼容的值
内存占用 分配 sizeof(int) * N 字节 分配 sizeof(int) 字节
用途 存储多个同类型元素 存储单个整数值
1. 洛谷P1113-杂物排序(模板)
cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4+5;
int n;
vector<int> edges[N];//任务i的后置任务
int len[N];//完成任务i的时间
int pre[N];//前置任务的数量
int mt[N];//完成i任务最少的时间
int max_time = 0;
void topoSort() {
queue<int> q;
for (int i = 1; i <= n; i++) {
if (pre[i] == 0) {
q.push(i);
mt[i] = len[i];
}
}
while (!q.empty()) {
int t = q.front();
q.pop();
for (int v : edges[t]) {
mt[v] = max(mt[v], mt[t] + len[v]);
pre[v]--;
if (pre[v] == 0) {
q.push(v);
}
}
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
int a,b;
cin >> a >> len[i];
while (cin>>b&&b!=0) {
edges[b].emplace_back(a);
pre[a]++;
}
}
topoSort();
for (int i = 1; i <= n; i++) {
max_time = max(max_time, mt[i]);
}
cout << max_time;
return 0;
}
LeetCode 207-课程表(判环)
cpp
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
const int N=2e3+5;
vector<int> edges[N];
vector<int> pre(numCourses, 0); // 初始化为0
for(int i=0;i<prerequisites.size();i++){
int u=prerequisites[i][0],v=prerequisites[i][1];
edges[v].emplace_back(u);
pre[u]++;
}
queue<int> q;
int cnt=0;
for(int i=0;i<numCourses;i++){
if(pre[i]==0){
q.push(i);
}
}
while(!q.empty()){
int t=q.front();
q.pop();
cnt++;
for(int v :edges[t]){
pre[v]--;
if(pre[v]==0){
q.push(v);
}
}
}
return cnt==numCourses;
}
};
Day21 暴力枚举优化
2021省赛-卡片拼数(预处理优化)
cpp
#include <iostream>
using namespace std;
int main() {
int cards[10] = {2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021}; // 所有数字初始为2021
int i = 1;
while (true) {
int x = i;
while (x) {
int digit = x % 10;
if (--cards[digit] < 0) { // 卡片不足
cout << i - 1 << endl;
return 0;
}
x /= 10;
}
i++;
}
return 0;
}
洛谷P2241-统计方形(数学推导)
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m;
cin>>n>>m;
long long z=0,c;
for(int i=1;i<=min(n,m);i++){
z+=(n-i+1)*(m-i+1);
}
c=n*m*(n+1)*(m+1)/4-z;
cout<<z<<" "<<c;
return 0;
}
2020省赛-七段码(枚举+并查集)上面并查集那里
Day22 模拟题专项
1.日期问题
cpp
#include <iostream>
using namespace std;
// 计算数字之和
int digitSum(int n) {
int sum = 0;
while (n > 0) {
sum += n % 10;
n /= 10;
}
return sum;
}
// 判断是否为闰年
bool isLeapYear(int year) {
return (year % 400 == 0) || (year % 100 != 0 && year % 4 == 0);
}
int main() {
int count = 0;
for (int year = 1900; year <= 9999; year++) {
int sum_year = digitSum(year);
for (int month = 1; month <= 12; month++) {
int sum_month = digitSum(month);
int max_day;
// 计算当前月份的最大天数
if (month == 2) {
max_day = isLeapYear(year) ? 29 : 28;
} else if (month == 4 || month == 6 || month == 9 || month == 11) {
max_day = 30;
} else {
max_day = 31;
}
for (int day = 1; day <= max_day; day++) {
int sum_day = digitSum(day);
if (sum_year == sum_month + sum_day) {
count++;
}
}
}
}
cout << count << endl;
return 0;
}
2.位运算(看看就好)
cpp
#include <iostream>
#include <bitset>
#include <vector>
using namespace std;
// 补码转换函数:将负数字节转为8位补码二进制字符串
string byteToBinary(int byte) {
if (byte >= 0) {
return bitset<8>(byte).to_string(); // 正数直接转8位二进制
}
else {
return bitset<8>(256 + byte).to_string(); // 负数补码处理(256 + byte等价于取反加1)
}
}
// 绘制一个汉字的16x16点阵
void printHanzi(const vector<int>& bytes) {
for (int i = 0; i < 16; i++) {
int byte1 = bytes[2 * i];
int byte2 = bytes[2 * i + 1];
string row = byteToBinary(byte1) + byteToBinary(byte2);
for (char bit : row) {
cout << (bit == '1' ? "██" : " "); // 1用██表示,0用空格
}
cout << endl;
}
}
int main() {
// 10个汉字的32字节数据(每个汉字32字节)
vector<vector<int>> hanzi_data = {
{4,0,4,0,4,0,4,32,-1,-16,4,32,4,32,4,32,4,32,4,32,8,32,8,32,16,34,16,34,32,30,-64,0},
{16,64,16,64,34,68,127,126,66,-124,67,4,66,4,66,-124,126,100,66,36,66,4,66,4,66,4,126,4,66,40,0,16},
{4,0,4,0,4,0,4,32,-1,-16,4,32,4,32,4,32,4,32,4,32,8,32,8,32,16,34,16,34,32,30,-64,0},
{0,-128,64,-128,48,-128,17,8,1,-4,2,8,8,80,16,64,32,64,-32,64,32,-96,32,-96,33,16,34,8,36,14,40,4},
{4,0,3,0,1,0,0,4,-1,-2,4,0,4,16,7,-8,4,16,4,16,4,16,8,16,8,16,16,16,32,-96,64,64},
{16,64,20,72,62,-4,73,32,5,16,1,0,63,-8,1,0,-1,-2,0,64,0,80,63,-8,8,64,4,64,1,64,0,-128},
{0,16,63,-8,1,0,1,0,1,0,1,4,-1,-2,1,0,1,0,1,0,1,0,1,0,1,0,1,0,5,0,2,0},
{2,0,2,0,7,-16,8,32,24,64,37,-128,2,-128,12,-128,113,-4,2,8,12,16,18,32,33,-64,1,0,14,0,112,0},
{1,0,1,0,1,0,9,32,9,16,17,12,17,4,33,16,65,16,1,32,1,64,0,-128,1,0,2,0,12,0,112,0},
{0,0,0,0,7,-16,24,24,48,12,56,12,0,56,0,-32,0,-64,0,-128,0,0,0,0,1,-128,3,-64,1,-128,0,0}
};
// 逐个绘制汉字
for (int i = 0; i < 10; i++) {
cout << "汉字 " << i + 1 << ":" << endl;
printHanzi(hanzi_data[i]);
cout << endl;
}
return 0;
}
其他题目
洛谷P1177 【模板】排序(快排)
使用指针 int* num
来表示待排序的数组
cpp
#include<bits/stdc++.h>
using namespace std;
int part(int* num, int l, int r) {
int point = num[l],i=l,j=r;
while (i < j) {
while (i<j && point<num[j]) {
--j;
}
if (i < j) {
swap(num[i++], num[j]);
}
while (i<j && point >=num[i]) {
++i;
}
if (i < j) {
swap(num[j--], num[i]);
}
}
return i;
}
void qsort(int * num,int l,int r) {
if (l < r) {
int mid = part(num, l, r);
qsort(num, l, mid - 1);
qsort(num, mid + 1, r);
}
}
int main() {
int N;
cin >> N;
int num[100001];
for (int i = 0; i <N; i++) {
cin >> num[i];
}
qsort(num,0,N-1);
for (int i = 0; i < N; i++) {
if (i != N - 1) {
cout<< num[i]<<" ";
}
else {
cout << num[i] << endl;
}
}
return 0;
}
前缀和:洛谷P1115 最大子段和(巧妙)
cpp
#include<bits/stdc++.h>
using namespace std;
int main() {
//引入前缀和这个概念,说白了就是自己及前面所有元素的的和,最大子数组和就是以某个元素结尾的前缀和减去不以这个元素为结尾的最小前缀和
int n;
int pre_sum = 0;
int min_pre = 0;
int res = -100001;//不能初始为0,因为如果第一个就是-100000,那么pre_sum=-100000,min_pre=0,如果初始为0,res=max(0,-100000)=0那么这个时候就是空的子数组,不符合题意
cin >> n;
int num[200001];
for (int i = 1; i <=n; i++) {
cin >> num[i];
min_pre = min(pre_sum, min_pre);
pre_sum += num[i];
res = max(res, pre_sum - min_pre);
}
cout << res;
return 0;
}
归并排序:洛谷P1177(归并实现)
详解里面递归看不懂看这个;

cpp
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
ll num[100001], num1[100001];
void merge(ll low, ll high) {
ll i = low, mid = (high + low) / 2, j = mid + 1, k = low;
while (i <= mid && j <= high) {
if (num[i] < num[j]) {
num1[k++] = num[i++];
}
else {
num1[k++] = num[j++];
}
}
while (i <= mid) {
num1[k++] = num[i++];
}
while (j <= high) {
num1[k++] = num[j++];
}
for (int i = low; i <= high; i++) {
num[i] = num1[i];
}
}
void merge_sort(ll low, ll high) {
if (low < high) {
ll mid = (high + low) / 2;
merge_sort(low, mid);
merge_sort(mid + 1, high);
merge(low, high);
}
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> num[i];
}
merge_sort(0, n - 1);
for (int i = 0; i < n; i++) {
if (i != n - 1)cout << num[i] << " ";
else cout << num[i];
}
return 0;
}
完全背包:洛谷P1616 疯狂的采药
cpp
#include <iostream>
using namespace std;
typedef long long ll;
int main(){
ll t,m;
cin>>t>>m;
ll time[10001];
ll value[10001];
ll dp[10000001]={0};
for(int i=0;i<m;i++){
cin>>time[i]>>value[i];
for(int j=time[i];j<=t;j++){
dp[j]=max(dp[j],dp[j-time[i]]+value[i]);
}
}
cout<<dp[t];
return 0;
}