当前位置: 首页 > news >正文

笔试强训:Week-4

目录

一*、小易的升级之路(数学)

二、礼物的最大价值(路径dp)

三*、对称之美(双指针/哈希)

四、经此一役小红所向无敌

五*、连续子数组最大和(线性dp)

六*、非对称之美(贪心+找规律)

七*、爱丽丝的人偶(贪心)

八*、集合(排序+双指针 / set去重)

九、最长回文子序列(区间dp)

十*、添加字符(枚举)

十一*、数组变换(贪心+位运算+数学)

十二、装箱问题(01背包)

十三*、打怪(模拟+数学)

十四、字符串分类(排序+哈希)

十五*、城市群数量(floodfill)

十六、判断是不是平衡二叉树(递归)

十七、最大子矩阵(二维前缀和)

十八*、小葱的01串(定长滑动窗口)


一*、小易的升级之路(数学)

小易的升级之路_牛客题霸_牛客网

就是求最大公约数,直接用gcd函数,不过得记得头文件 #include<numeric>

如果是暴力的话会超时,或者手搓一个gcd函数,这里推荐使用欧几里得算法,时间复杂度最低。

可以进一步优化,说实话还是感觉上面的代码更好理解。

记得这种算法可以用装修用固定大小的瓷砖填地板解释,或者该用切蛋糕来解释吧,比如我有一块24x16(cm)的蛋糕,要求切成大小相同的正方形蛋糕,但不能剩余,问切出的蛋糕最大边长是多少?答案就是24和16的最大公约数。而欧几里得算法得到这个最大公约数的求解过程,就可以理解为:

第一次用较小的边尝试,切出一块16x16的蛋糕,但是有剩余,不符合题意,那么我们就要去更小的那部分里边找边长更小的正方形(这里边找出的结果,最终在更大的那部分比如这里是16x16,一定能切出很多结果边长大小的正方形,并且一定不会有剩余),然后继续在小的里边继续切,思路依然是用小的那条边当作大小来切。然后找到8,8可以,且较大边%8==0,a=temp(8),b=a%b也就是等于0,然后下一次循环,b==0退出循环,a==8就是最终结果。也就是说,我们每次都选择较小边当作切成的蛋糕大小,如果没有剩余,那么此时就是最大公约数。如果有剩余,那么我们以剩下那部分的两条边长作为参数,不断进行这种操作,直到出现结果。妙啊。

代码实现如下:

#include <iostream>
#include <numeric>
using namespace std;
using LL=long long;
int main() {LL n,x;cin>>n>>x;LL Mlevel;for(int i=0;i<n;++i){cin>>Mlevel;if(x>=Mlevel)x+=Mlevel;else{x+=gcd(x, Mlevel);}}cout<<x;return 0;
}

二、礼物的最大价值(路径dp)

礼物的最大价值_牛客题霸_牛客网

相当基础的路径dp题,不知道是不是第一天所以这么简单?

#include <vector>
class Solution {
public:int maxValue(vector<vector<int> >& grid) {int m=grid.size(),n=grid[0].size();vector<vector<int>> dp(m+1,vector<int>(n+1));for(int i=1;i<=m;++i){for(int j=1;j<=n;++j){dp[i][j]=grid[i-1][j-1]+max(dp[i-1][j],dp[i][j-1]);}}return dp[m][n];}
};

三*、对称之美(双指针/哈希)

对称之美

解法一:双指针

开始还以为要dfs全排列,然后判断是不是回文,心想这太复杂了,然后写出dfs全排列+判断回文的代码,运行过了ok提交,果不其然超时了。然后想着想着不就是判断1对应n-1,2对应n-2以此类推判断是不是有重复元素嘛,这样现将字符串存进数组,然后用双指针就能搞定了,写一个判断回文的函数不就是判断有没有Same

代码实现如下:

#include <iostream>
#include <ostream>
using namespace std;
#include<vector>
#include<algorithm>
bool HasSameChar(string s1,string s2)
{sort(s1.begin(),s1.end());sort(s2.begin(),s2.end());int cur1=0,cur2=0;while(cur1<s1.size()&&cur2<s2.size()){if(s1[cur1]<s2[cur2])++cur1;else if(s1[cur1]>s2[cur2])++cur2;else return true;} return false; 
}
int main() {int t;cin>>t;int n;while(t--){cin>>n;vector<string> s(n);for(int i=0;i<n;++i)cin>>s[i];//因为输出的二元性,所以我用bool变量来标识bool flag=true;int left=0,right=n-1;while(left<right){if(!HasSameChar(s[left], s[right])){flag=false;break;}++left;--right;}if(flag)cout<<"Yes"<<endl;else cout<<"No"<<endl;}return 0;
}

解法二:哈希

判重判重,第一个想到的不就是哈希嘛

我们用一个二维数组来模拟哈希表的思路,第一个位置表示第几个字符串,第二个位置用于表示哪个字母,所以我们开[101][26]这样的大小

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
bool vis[110][26];//用来标记每一行 字母是否存在
int t,n;
string s;
bool check(int left,int right){//26个字母暴力检查有没有for(int i=0;i<26;++i)if(vis[left][i]&&vis[right][i]) return true;return false;
}
int main(){cin>>t;while(t--){//先清空hash表memset(vis,0,sizeof vis);cin>>n;//知道了有多少个字符串for(int i=0;i<n;++i){cin>>s;for(auto&ch:s) vis[i][ch-'a']=true;}//双指针向中间靠int left=0,right=n-1;for(;left<right;++left,--right)if(!check(left,right)) break;//left<right为假说明只是越界导致结束,输出Yescout<<(left<right?"No":"Yes")<<endl;}return 0;
}

今天三道都写出来了,希望明天也能都写出来hhh\(^o^)/~

实则不然

四、经此一役小红所向无敌

经此一役小红所向无敌

模拟:会超时

#include <iostream>
using namespace std;
using LL=long long;
int main() {LL a,h,b,k;cin>>a>>h>>b>>k;LL sum=0;while(h&&k){sum+=(a+b);h-=b;k-=a;if(h<=0&&k<=0)break;if(h<=0&&k){sum+=(b*10);break;}if(k<=0&&h){sum+=(a*10);break;}}cout<<sum;return 0;
}

优化一下就是,提前统计一下互砍的次数,取min

#include<iostream>
using namespace std;
typedef long long LL;
LL a,h,b,k;
int main(){cin>>a>>h>>b>>k;LL ret=0;//累计伤害//看看两个人能互砍多少回合LL n=min(h/b,k/a);ret+=n*(a+b);//计算剩余血量h-=b*n;k-=a*n;//看看是不是都活着 如果都活着就再砍一下if(h>0&&k>0){h-=b;k-=a;ret+=a+b;}//这时候至少死了一个  或者两个都死了if(h>0||k>0)  ret+=10*(h>0?a:b);cout<<ret<<endl;return 0;
}

五*、连续子数组最大和(线性dp)

连续子数组最大和(ACM版本)_牛客题霸_牛客网

没有做出来,看来近期要复习dp了。。。

#include <iostream>
#include <algorithm>
using namespace std;
const int N=2e5+10;
int nums[N],dp[N];
int n;
int main() {cin>>n;int ret=-101;for(int i=1;i<=n;++i) cin>>nums[i];for(int i=1;i<=n;++i){dp[i]=max(dp[i-1],0)+nums[i];ret=max(dp[i],ret);}cout<<ret<<endl;return 0;
}

六*、非对称之美(贪心+找规律)

非对称之美

所谓题干越短,杀伤力越强?

情况1:所以字符都一样 return 0

情况2:整个串都是回文 return n-1

情况3:整个串不是回文 return n 

佩服。。。。这就是非对称之美吗

#include<iostream>
#include<string>
using namespace std;
string s;
int func(){int n=s.size();//首先要判断是否全都相同int i=1;for(;i<n;++i)if(s[i]!=s[0]) break;if(i==n) return 0;//接下来双指针往中间靠,判断整个字符串是否都是回文串int left=0,right=n-1;for(;left<right;++left,--right)if(s[left]!=s[right]) break;return left<right?n:n-1;
}
int main(){cin>>s;cout<<func()<<endl;return 0;
}

七*、爱丽丝的人偶(贪心)

爱丽丝的人偶

没有思路

看完解答后发现只要让数组的数据是波形的就行

#include<iostream>
using namespace std;
int n;
int main(){cin>>n;//必须有单调性,所以就一高一矮 这样放int left=1,right=n;while(left<=right){cout<<left++<<" ";if(left<=right) cout<<right--<<" ";}return 0;
}

八*、集合(排序+双指针 / set去重)

集合_牛客题霸_牛客网

解法一:排序+双指针

因为使用双指针算法,不要忘记排序。此外,注意可能会有重复元素!

其实这也是一种归并思路

#include <iostream>
#include <algorithm>
using namespace std;
int n,m;
const int N=1e4+10;
int a[N],b[N];
int main() {cin>>n>>m;for(int i=0;i<n;++i) cin>>a[i];for(int i=0;i<m;++i) cin>>b[i];sort(a,a+n);sort(b,b+m);//双指针归并 小的插入后++  int cur1=0,cur2=0;while(cur1<n&&cur2<m){if(a[cur1]<b[cur2]){cout<<a[cur1++]<<" ";while(a[cur1]==a[cur1-1]) ++cur1;} else if(a[cur1]>b[cur2]){cout<<b[cur2++]<<" ";while(b[cur2]==a[cur2-1]) ++cur2;}else{cout<<a[cur1++]<<" ";++cur2;while(a[cur1]==a[cur1-1]) ++cur1;while(b[cur2]==a[cur2-1]) ++cur2;}}//有一组还没走完while(cur1<n){if(cur1==0||a[cur1]!=a[cur1-1]) cout<<a[cur1]<<" ";++cur1;}while(cur2<m){if(cur2==0||b[cur2]!=b[cur2-1]) cout<<b[cur2]<<" ";++cur2;}return 0;
}
// 64 位输出请用 printf("%lld")

解法二:set(set容器默认对数据升序排序,而且会去重)

#include <iostream>
#include <set>
using namespace std;
int n,m;
set<int> s;
int main() {cin>>n>>m;int x;while(n--){cin>>x;s.insert(x);}while(m--){cin>>x;s.insert(x);}for(auto&e:s) cout<<e<<" ";return 0;
}
// 64 位输出请用 printf("%lld")

九、最长回文子序列(区间dp)

最长回文子序列_牛客题霸_牛客网

没想到自己一遍做出来了,开心啊

#include <iostream>
#include <vector>
using namespace std;int main() {//dp[i][j]表示i~j区域内的最长回文子序列string s;cin>>s;int n=s.size(),len=1;vector<vector<int>> dp(n,vector<int>(n,1));//因为第一个位置会使用后续的位置,所以倒着填for(int i=n-1;i>=0;--i){for(int j=i+1;j<n;++j){if(s[i]==s[j]){dp[i][j]=i+1<j?dp[i+1][j-1]+2:2;}else dp[i][j]=max(dp[i+1][j],dp[i][j-1]);len=max(len,dp[i][j]);}}   cout<<len;return 0;
}

十*、添加字符(枚举)

添加字符_牛客笔试题_牛客网

本来以为是匹配最长的连续的长度,然后字符串长度减去求得的最长长度来做,但是匹配可以在A串的任何一个位置匹配,如果第一个位置不同但后续相同,代码就出错了,比如xello和hello,最终输出5因为求得最长长度为0,但答案应该为1所以还是枚举吧

#include <iostream>
#include <string>
using namespace std;
string a,b;
int main() {cin>>a>>b;int m=a.size(),n=b.size();//不相等的最多就是m个int ret=m;for(int i=0;i<=n-m;++i){//枚举b的起始位置int tmp=0;//统计不同位数的个数for(int j=0;j<m;++j)if(a[j]!=b[i+j]) ++tmp;ret=min(ret,tmp);}cout<<ret<<endl;
}
// 64 位输出请用 printf("%lld")

十一*、数组变换(贪心+位运算+数学)

数组变换__牛客网

贪心:因为我们每次都需要在一个大一个小的数里面,看小的数不断x2操作能不能达到更大的那个数,因为如果两个数通过乘2操作能相等,那么小的数乘以一定次数的2就能等于大的数。那么现在这个“更大的数”可能会有更大的数,那么就要看这个大的数能不能不断x2操作等于它的更大的数。所以我们只需要找到数组里的最大值,然后看其他数能不能通过x2操作达到这个值即可。那么我们只需要判断MAXNUM/arr[i]能不能被2整除即可。

如何判断被2整除?

x&(x-1)==0就代表最高位是1其他全是0,这就是2的倍数。

或者 x-(x&-x)==0,x&-x得到x低位到高位第一个1,如果减去后为0,就说明只有最高位是1,其他位是0,是2的倍数。

#include <iostream>
using namespace std;
int a[51];
int n,maxval;
bool func(){for(int i=0;i<n;++i){if(maxval%a[i]) return false;int x=maxval/a[i];//if(x&(x-1)) return false;if(x-(x&-x)) return false;}return true;
}
int main() {cin>>n;for(int i=0;i<n;++i){cin>>a[i];maxval=max(maxval,a[i]);}cout<<(func()?"YES":"NO")<<endl;
}
// 64 位输出请用 printf("%lld")

十二、装箱问题(01背包)

装箱问题

#include<iostream>
using namespace std;
//dp[i][j]表示选前i个物品 容积不超过j  所占的最大容积
const int N=2e4+1;
int arr[31];
int dp[N];
int v,n;
int main(){cin>>v>>n;for(int i=1;i<=n;++i) cin>>arr[i];for(int i=1;i<=n;++i)for(int j=v;j>=arr[i];--j)dp[j]=max(dp[j],dp[j-arr[i]]+arr[i]);cout<<v-dp[v]<<endl;
}

十三*、打怪(模拟+数学)

打怪

monimonimoni,想这个公式模拟了好久,估计是没睡饱

#include <iostream>
using namespace std;int main() {int t;cin>>t;while(t--){int h,a,H,A;cin>>h>>a>>H>>A;if(A==0||a>=H){cout<<-1<<endl;continue;}//times每次杀死怪需要几次攻击//times-1对应杀死一只怪受到几次伤害int times=H/a+(H%a?1:0);//blood是杀一次怪扣的血//判断最后一次是否符合,如果刚好可以取模,代表第x个怪的时候,勇者先死,减去这一次int blood=(times-1)*A;int x=h/blood+(h%blood==0?-1:0);cout<<x<<endl;}return 0;
}

十四、字符串分类(排序+哈希)

字符串分类_牛客笔试题_牛客网

很简单的题目

#include <iostream>
using namespace std;
#include<algorithm>
#include<unordered_map>
int main() {int n;string s;unordered_map<string, int> hash;cin>>n;while(n--){cin>>s;sort(s.begin(),s.end());hash[s]++;}cout<<hash.size();return 0;
}

十五*、城市群数量(floodfill)

城市群数量_牛客题霸_牛客网

原本以为是遍历下三角,然后统计一下入度,再从1~n里查找入度为0的数量,这样就是城市群的数量。但是这是不正确的。因为:

该题为无向边,没有出度还有入度的概念,所以不能这样统计。

比如2指向1,3指向1,那么此时城市群的数量应该是1.但是按照我们统计入度为0的个数,算出的是2错误

正确解法是floodfill问题的思路,用vis标记连接的城市群

class Solution {
public:
//联通块问题
bool vis[201]={0};//标记每个城市是否被搜索过
int n;int citys(vector<vector<int>>& nums) {n=nums.size();int ret=0;for(int i=0;i<n;++i)if(!vis[i]){++ret;dfs(nums,i);//没被搜索过 就从该点开始找联通块}return ret;}void dfs(vector<vector<int>>& nums,int pos){vis[pos]=true;for(int i=0;i<n;++i)if(!vis[i]&&nums[pos][i]) dfs(nums,i);}
};

今天三道题也是稳稳拿下

十六、判断是不是平衡二叉树(递归)

判断是不是平衡二叉树_牛客题霸_牛客网

很简单

class Solution {
public:int GetDepth(TreeNode*root){if(!root)return 0;return 1+max(GetDepth(root->left),GetDepth(root->right));}bool IsBalanced_Solution(TreeNode* root) {if(!root)return true;if(!IsBalanced_Solution(root->left))return false;if(!IsBalanced_Solution(root->right))return false;int LeftDepth=GetDepth(root->left),RightDepth=GetDepth(root->right);if(abs(LeftDepth-RightDepth)>1)return false;return true;}
};

十七、最大子矩阵(二维前缀和)

最大子矩阵_牛客题霸_牛客网

一遍过

#include <climits>
#include <iostream>
using namespace std;
#include<vector>
int main() {int N;cin>>N;vector<vector<int>> martix(N+1,vector<int>(N+1));auto FrontSum=martix;int res=INT_MIN;for(int i=1;i<=N;++i){for(int j=1;j<=N;++j){cin>>martix[i][j];//计算前缀和FrontSum[i][j]=FrontSum[i-1][j]+FrontSum[i][j-1]-FrontSum[i-1][j-1]+martix[i][j];res=max(res,FrontSum[i][j]);for(int x=1;x<=i;++x){for(int y=1;y<=j;++y){//计算区间前缀和int Sum=FrontSum[i][j]-FrontSum[i][y-1]-FrontSum[x-1][j]+FrontSum[x-1][y-1];res=max(res,Sum);}}}}cout<<res;return 0;
}

十八*、小葱的01串(定长滑动窗口)

小葱的01串

看完题目觉得就是求定长窗口0,1数量如果等于0,1总数的一半就++计数器,然后最后输出计数器乘以2?应该不对,如果头位置和尾位置在窗口里且符合的时候,其实是同一种情况。只有窗口在不包含头尾的中间位置的时候的解决方案才是需要*2的。

#include <iostream>
using namespace std;int main() {int n;cin>>n;string s;cin>>s;int Num0=0,Num1=0,count0=0,count1=0,solves=0;for(auto&ch:s){if(ch-'0')++Num1;else ++Num0;}for(int i=0;i<n/2;++i){if(s[i]=='0')++count0;else ++count1;}int right=n/2,left=0;int flag=0;if(count0==Num0/2&&count1==Num1/2){++solves;flag=2;}while(right<n){if(s[right]=='0')++count0;else ++count1;if(s[left]=='0')--count0;else --count1;if(count0==Num0/2&&count1==Num1/2)++solves;++right;++left;}cout<<(solves-flag)*2+flag;return 0;
}

或者说,因为1和n-1包含在窗口里时是属于同一种情况,我们只需要从0遍历到n-2的位置即可

#include <iostream>
using namespace std;int main() {int n;cin>>n;string s;cin>>s;int Num0=0,Num1=0,count0=0,count1=0,solves=0;for(auto&ch:s){if(ch-'0')++Num1;else ++Num0;}for(int i=0;i<n/2-1;++i){if(s[i]=='0')++count0;else ++count1;}int left=0,right=n/2-1;while(right<n-1){if(s[right]=='0')++count0;else ++count1;if(count0==Num0/2&&count1==Num1/2)++solves;if(s[left]=='0')--count0;else --count1;++right;++left;}cout<<solves*2;return 0;
}

本周结束!完结撒花✿✿ヽ(°▽°)ノ✿

http://www.dtcms.com/a/583278.html

相关文章:

  • 网站建设投票系统总结二级目录网站怎么做
  • 【C++】:C++聊天室后台服务器之Spdlog日志组件安装与使用
  • 青岛建设工程信息网站如何做网站步骤
  • shell(2)--变量、算数运算、测试表达式、常用的操作运算符、文件测试运算符、整数值比较
  • php怎样做网站管理后台设计网站软件开发
  • 有没有和小孩做的网站新闻发布会邀请哪些媒体
  • 浙江做铁塔的公司网站网站年费如何做会计分录
  • 开州网站建设网站项目有需要什么技术支持
  • Linux27 线程同步--条件变量
  • seo网站优化推广怎么做盐城网站建设有限公司
  • Numpy一维、二维、三维数组切片实例
  • 手机端网站建站云南网站建设招商
  • 开放获取 SuperMamba 小目标检测特征增强框架
  • 布吉网站建设哪家技术好怎样在手机上创建网站
  • seo是什么?seo网站关键词优化哪家好
  • MinimalWalls v1.9.8 | 提供高质量简约壁纸,支持一键下载、收藏和自动更换等功能,界面干净并支持深浅模式切换
  • 网站优化推广seo公司网站建设的有什么需求
  • 成都网站建设与网站制作网站建设的技术要求
  • 东营住房与城乡建设部网站网站优化 北京
  • asp网站免费模板专门做外链的网站
  • 网站开发技术课程设计说明书做卖挖掘机的网站
  • 购物网站功能模块图wordpress 中英文站点
  • 神经网络中的反向传播与梯度下降
  • 备案网站 备注内容wordpress主题的使用
  • Linux C/C++ 学习日记(48):dpdk(九):dpdk的应用场景及劣势
  • 台州网络建站模板一般网站建设流程有哪些步骤
  • 做视频添加字幕的网站网站建设费用:做个网站要多少钱?
  • 无锁编程在高并发场景下的性能优势
  • Linux:WSL内存空间管理之清完内存C盘可用空间不增问题解决
  • 女頻做的最好的网站iis7 新建网站