欢迎来到Cefler的博客😁
🕌博客主页:那个传说中的man的主页
🏠个人专栏:题目解析
🌎推荐文章:题目大解析(3)
目录
👉🏻最大降雨量
原题链接:最大降雨量
解题思路:
这里假设7周的中位数分别为a,b,c,d,e,f,g。
这里e,f,g必然要大于中位数d。但我们如何实现让d最大呢?
想让d最大,我们换种角度看,就是49张牌中最少几张必须大于d,
显然我们可以看到后三周e,f,g后四天(包括e,f,g本身)这些必须大于d,还有d的后3天也必须大于d.
所以共有3x4+3 = 15天必须大于d,所以d的最大值为49-15 = 34.
👉🏻既约分数
#include <iostream>
using namespace std;
int fun(int a,int b)
{
while(b)
{
int k = a%b;
a = b;
b = k;
}
return a;
}
int main()
{
// 请在此输入您的代码
int count = 0;
for(int i = 1;i<=2020;i++)
{
for(int j = 1;j<=2020;j++)
{
if(fun(i,j)==1)
count++;
}
}
cout<<count<<endl;
return 0;
}
👉🏻冶炼金属 (2023B组真题)
mycode:
#include<iostream>
#include<vector>
#include<math.h>
using namespace std;
int main()
{
int N;
cin >> N;
vector<int> O(N), X(N);
//先确定最大值
int Max = 0;
for (int i = 0; i < N; i++)
{
cin >> O[i] >> X[i];
if (O[i] / X[i] > Max)
Max = O[i] / X[i];
}
int finalmax = 0, finalmin = pow(10,9);
for (int i = Max; i > 0; i--)
{
int flag = 1;
for (int j = 0; j < N; j++)
{
if ( O[j]/i != X[j])
{
flag = 0;
break;
}
}
if (flag)
{
if (i > finalmax) finalmax = i;
if (i < finalmin) finalmin = i;
}
}
printf("%d %d\n", finalmin, finalmax);
return 0;
}
👉🏻飞机降落(广度遍历和回溯)
mycode:
#include<iostream>
#include<vector>
#include<stdio.h>
using namespace std;
typedef struct Inf
{
int t;
int d;
int l;
}inf;
bool check[10];//检查飞机是否被用过
vector<inf> v;
bool dfs(int sz, int last)
{
if (sz == v.size())//走到这里说明已经有三个都可以了
return true;
for (int i = 0; i < v.size(); i++)
{
if (!check[i]&&v[i].t + v[i].d >= last)
{
check[i] = true;
if (dfs(sz + 1, max(last, v[i].t) + v[i].l)) return true;
//回溯恢复现场
check[i] = false;
}
}
return false;
}
int main()
{
int n;
cin >> n;
while (n--)
{
int num;
cin >> num;
v.resize(num);
for (int i = 0; i < num; i++)
cin >> v[i].t >> v[i].d >> v[i].l;
if (dfs(0, 0))
cout << "YES" << endl;
else
cout << "NO" << endl;
memset(check, 0, sizeof check);
}
return 0;
}
#include <bits/stdc++.h>
是万能头文件
👉🏻接龙数列
mycode1(广度遍历超时):
#include <bits/stdc++.h>
using namespace std;
int N;
vector<string> v;
int Min;// 删掉最少的数
bool erase[100000];//记录删除位置
bool IsSerial(vector<string>& v)//判断是否为接龙数列
{
vector<string> tmp;
for (int i = 0; i < N; i++)
{
if (erase[i] == false)
{
tmp.push_back(v[i]);
}
}
int sz = (int)tmp.size();//注意:vector的size返回值是size_t
if (sz >1)
{
for (int i = 0; i < sz-1; i++)
{
if (tmp[i].back() != tmp[i + 1].front())
return false;
}
}
return true;
}
void dfs()//广度遍历所有删除可能性
{
for (int i = 0; i < N; i++)
{
if (!erase[i])//如果不是接龙队列,删除
{
if (!IsSerial(v))
{
erase[i] = true;//将这个数删除
dfs();
//恢复现场
erase[i] = false;
}
else
{
//此时已经删到是接龙队列了
//遍历erase数组,记录true的数量,但是最后要再减1,因为再减1才能恢复接龙数列
int num = 0;
for (int i = 0; i < N; i++)
{
if (erase[i] == true) num++;
}
if ( num < Min) Min = num;
break;//此时直接退出即可
}
}
}
}
int main()
{
cin >> N;
v.resize(N);
Min = N;
for (int i = 0; i < N; i++)//26 61 14 41 15
cin >> v[i];
if (!IsSerial(v))
dfs();
else
Min = 0;//不用删了
cout << Min << endl;
return 0;
}
mycode2:(动态规划但仍然超时)
#include<iostream>
#include<vector>
using namespace std;
int main()
{
//dp[i]:表示到i位置及之前最长的接龙数列长度 14 47 27 75
//dp[i] 特征方程表示: i之前的接龙数列长度一定是<=dp[i-1]的,所以dp[i]要么是dp[i-1]+1要么就是dp[i-1]
//所以如何判断是否要加1很重要:我们先将dp表全部初始化为1,因为我们将每一个独立元素视为一个接龙队列,我们将每一个接龙队列最后一个元素称为龙头
//每个龙头一开始都为1,但是我们在dp遍历过程中,我们要从当前位置向前再遍历,去寻找前面队列中能接龙上并且龙头值最大的队列,1+该龙头值即当前位置的最大龙头值
//设这个表示龙头值的容器名为dragon,最后dp[i] = max(dragon[i],dp[i-1]);
//最后结果就是N-dp[N-1]
int N;
cin >> N;
vector<string> v(N);
for (int i = 0; i < N; i++)
cin >> v[i];
vector<int> dp(N),dragon(N,1);
//dp表初始化
dp[0] = 1;//第一个位置肯定为1
//开始填表
for (int i = 1; i < N; i++)
{
for (int j = i-1; j >= 0; j--)
{
if (v[i].front() == v[j].back())
dragon[i] = max(dragon[j] + 1, dragon[i]);
}
dp[i] = max(dragon[i], dp[i - 1]);
}
cout << N - dp[N - 1] << endl;
return 0;
}
动态规划极简线性dp(参考答案)
#include <bits/stdc++.h>
using namespace std;
int dp[10];
int main () {
int n, mx = 0; cin >> n;
for (int i = 0; i < n; i ++) {
string s; cin >> s;
int a = s[0] - '0', b = s.back() - '0';
dp[b] = max(dp[b], dp[a] + 1), mx = max(mx, dp[b]);
}
cout << n - mx << endl;
return 0;
}
👉🏻岛屿个数
mycode:
#include<bits/stdc++.h>
#include<iostream>
#include<vector>
using namespace std;
#define SZ 52
int M, N;
int island = 0;
bool check[SZ][SZ];
bool checkofspread[SZ][SZ];
char circle[SZ][SZ];
//上 下 左 右 左上 右上 左下 右下
int dx[8] = {-1,1,0,0,-1,1,-1,1};
int dy[8] = { 0,0,-1,1,-1,-1,1,1 };
bool spread(int row, int col)
{
for (int i = 0; i < 8; i++)
{
int x = row + dx[i], y = col + dy[i];
if (!(x >= 0 && x < M && y >= 0 && y < N))//跳出地图了
return true;
else if ((x >= 0 && x < M && y >= 0 && y < N) && !checkofspread[x][y] && circle[x][y] != 'o')
{
checkofspread[x][y] = true;
return spread(x, y);
}
}
//八方都不行的话,只能认命了
return false;
}
bool dfs(vector<vector<int>>& vv,int row,int col,int initial[],int step)
{
for (int i = 0; i < 4; i++)
{
int x = row + dx[i], y = col + dy[i];
if (x >= 0 && x < M && y >= 0 && y < N && !check[x][y] && vv[x][y] == 1)//1.坐标合法 2.为1且未被check过
{
check[x][y] = true;
step++;
if (dfs(vv, x, y, initial, step))
{
circle[x][y] = 'o';
return true;
}
}
else if (x >= 0 && x < M && y >= 0 && y < N && check[x][y] && x == initial[0]&&y==initial[1] &&vv[x][y] == 1)//如果已经是被check过了,递归到了闭环,回到了原点
{
if(step>3)//步数至少大于3的时候才形成一个闭环
return true;//说明是闭环
}
}
//上下左右都走不了的话,说明该节点不属于环节点
return false;
}
int main()
{
int n;
cin >> n;
while (n--)
{
cin >> M >> N;
vector<string> s(M);
for (int i = 0; i < M; i++)
cin >> s[i];
vector<vector<int>> vv(M, vector<int>(N));
for (int i = 0; i < M; i++)
for (int j = 0; j < N; j++)
vv[i][j] = s[i][j]-'0';
//遍历
for (int i = 0; i < M; i++)
{
for (int j = 0; j < N; j++)
{
if (!check[i][j] && vv[i][j] == 1)
{
check[i][j] = true;
int initial[] = { i,j };//这个记录最初出发的节点
//如果是环节点,递归后,一开始出发后会有两个回溯,step>3时,才是递归形成闭环
if (dfs(vv, i, j,initial,0))
{
circle[i][j] = 'o';
}
//现在就让从该位置向四面八法散开,遇到环节点不能扩散,直到有一个坐标能扩散出整个地图,说明该岛屿并没有被环绕住
if (spread(i, j))
island++;
memset(checkofspread, 0, sizeof checkofspread);
}
}
}
cout << island << endl;
//重置初始化
memset(check, 0,sizeof check);
memset(circle, ' ', sizeof circle);
island = 0;
}
return 0;
已经尽力了,太难了[苦]
👉🏻统计子矩阵
mycode(超出时间限制):
#include<iostream>
#include<vector>
using namespace std;
int GetSum(vector<vector<int>>& vv,int r,int c,int size_r,int size_c)
{
int sum = 0;
for(int i = r;i<=size_r;i++)
{
for(int j = c;j<=size_c;j++)
sum+= vv[i][j];
}
return sum;
}
int main()
{
int N,M,K;
cin>>N>>M>>K;
vector<vector<int>> vv(N+1,vector<int>(M+1));
for(int i = 1;i<=N;i++)
{
for(int j = 1;j<=M;j++)
{
cin>>vv[i][j];
}
}
int count = 0;
for(int i = 1;i<=N;i++)
{
for(int j = 1;j<=M;j++)
{
//i*j阶
for(int r = 1;r<=N;r++)
{
for(int c = 1;c<=M;c++)
{
if(r+i-1<=N&&c+j-1<=M&&GetSum(vv,r,c,r+i-1,c+j-1)<=K)
count++;
}
}
int here;
}
}
cout<<count<<endl;
return 0;
}
参考优质代码:
#include<stdio.h>
int s[505][505];
int main( )
{
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
scanf("%d",&s[i][j]);
s[i][j]+=s[i-1][j];
}
}
long long int res=0;
for(int i=1; i<=n; i++)
{
for(int j=i; j<=n; j++)
{
for(int left=1,right=1,sum=0; right<=m; right++)
{
sum+=s[j][right]-s[i-1][right];
while (sum>k)
{
sum-=s[j][left]-s[i-1][left];
left++;
}
res+=right-left+1;
}
}
}
printf("%lld",res);
return 0;
}
此代码主要思路就是
1.算s[i][j]:第j列上,从第0行至第i行所有元素之和
2.进行区间遍历,[i,i~n],i>=1,i<=n;
3.区间遍历中包含列遍历,列遍历是在当前行区间内,统计所有和<k的子矩阵数量
代码复现:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int s[501][501];
int n,m,k;
cin>>n>>m>>k;
for(int i = 1;i<=n;i++)
{
for(int j = 1;j<=m;j++)
{
cin>>s[i][j];
s[i][j]+=s[i-1][j];//计算列之和
}
}
//进行区间遍历[i,j]行区间
long long res = 0;
for(int i = 1;i<=n;i++)
{
for(int j = i;j<=n;j++)
{
for(int left = 1,right = 1,sum = 0;right<=m;right++)//列遍历
{
sum+=(s[j][right] - s[i-1][right]);//计算在right列上,[i,j]区间上的和
while(sum>k)
{
sum-=(s[j][left] - s[i-1][left]);
left++;
//这里前缀和思想非常巧妙
//在列遍历中,sum始终记录这该行区间内可能会出现的子矩阵之和
//而不为了影响后续的列遍历
//while循环内进行了断尾操作,比如我刚刚遍历第一列的元素没问题,但是到第二列sum>k了
//此时就可以放心把第一列的子矩阵之和删了,反正你已经ok了,我继续看看第二列可不可以 ,以此类推
}
res+=(right-left+1);//这个res+=也很妙
}
}
}
cout<<res<<endl;
return 0;
}