题目列表
数组
二分法
重点: 区间 [left,right]
右限值 right=middle-1
左限值 left=middle+1
标准二分法
while(left<=right){ //注意小于等于
int mid=(right-left)/2+left;
if(nums[mid]==tagert) //等于情况
return tagert;
else if(nums[mid]>tagert)
right=mid-1; //注意减一
else if(nums[mid]<tagert)
left=mid+1; //注意加一
}
旋转数组最小值二分法
存在重复元素
while(left<right){ //由于不处理等于情况所以不能相等
int mid=(right-left)/2+left;
if(nums[mid]>nums[right])
left=mid+1; //除的时候向下取余所以左边要缩,+1
else if(nums[mid]<nums[right])
right=mid; //无法排出这个mid就是最小值,所以不能缩
else if(nums[mid]==nums[right])
right--; //现在两个值相等,只能缩一个看看
}
原地移除
重点: 双重for循环,
nums[i] = =tagert ,则 j = i+1,寻找 nums[j] !=tagert, 并交换 nums[i] 与 nums[j]。
快慢指针 一个for循环遍历快指针,如果nums[fast] !=tagert,快慢指针进行交换 ,同时慢指针+1;
https://leetcode.cn/problems/remove-element/
最短长度的连续数组
重点: 双指针,–for加while 滑动窗口
right每个回合右移一次,总和大于等于target,则while循环 res-=nums[left++] while循环内部进行最短长度的比较保留。
https://leetcode.cn/problems/minimum-size-subarray-sum/
链表
链表二分法
if(head==nullptr)
return head;
listnode*slow=head,*fast=head;
while(fast->next!=nullptr&&fast->next->next!=nullptr)
{
slow=slow->next;
fast=fast->next->next;
}
slow为中心位置
https://leetcode.cn/problems/binary-search/
单链表排序
链表适合归并排序,先使用上面的链表二分然后划分分治,再由分治一路合并回去
ListNode* gubing(ListNode*root){
if(root==nullptr||root->next==nullptr)
return root;
//二分找到中点
ListNode*slow=root;
ListNode*fast=root;
while(fast->next!=nullptr&&fast->next->next!=nullptr){
slow=slow->next;
fast=fast->next->next;
}
ListNode*temp=slow->next;
slow->next=nullptr; //切开大链表
ListNode* left=gubing(root); //左半部分
ListNode* right=gubing(temp); //右半部分
ListNode*node=new ListNode(0); //留个头结点好返回 准备合并上面两部分的链表
root=node;
while(left!=nullptr&&right!=nullptr){
if(left->val<right->val){ //比较大小放进去
node->next=left;
left=left->next;
}else{
node->next=right;
right=right->next;
}
node=node->next;
}
//有那个要是短一截,就直接接上,然后返回头结点
if(left!=nullptr)
node->next=left;
if(right!=nullptr)
node->next=right;
return root->next;
}
ListNode* sortInList(ListNode* head) {
if(head==nullptr||head->next==nullptr)
return head;
return gubing(head);
}
topK问题
使用根堆最佳,前k个不用排序,后面N-K个也不用排序,只要找到K就行,维护一个小根堆,它就是前k个最大数里面最小的元素。每次跟根比较,要是替换了,就调整根堆
void gendui(vector<int>&a,int father,int boundary){
int son=father*2+1; //找到它的儿子
while(son<boundary){
if(son+1<boundary&&a[son+1]<a[son]){ //看看兄弟俩哪个更小,取哪个
son++;
}
if(a[father]>a[son]){ //爹要是比儿子大,就交换,然后一路查下去
int temp=a[father];
a[father]=a[son];
a[son]=temp;
father=son;
son=father*2+1;
}else{
break;
}
}
}
int findKth(vector<int>& a, int n, int K) {
vector<int>nums(a.begin(),a.begin()+K);
int count=K/2-1; //找第一个非叶子节点(父节点),就是n/2-1
for(int i=count;i>=0;i--){
gendui(nums,i,K);
}
//将后面的数每个都跟小根堆进行比较,根堆点为0
for(int i=K;i<n;i++){
if(nums[0]<a[i]){
nums[0]=a[i];
gendui(nums,0,K);
}
}
return nums[0];
}
移除链表元素
重点: 1.判断头指针是否要移除,2.pre->next!=null && pre!=null
https://leetcode.cn/problems/remove-linked-list-elements/
互换链表2个元素
重点: 头结点很重要,链表一定要用
https://leetcode.cn/problems/swap-nodes-in-pairs/submissions/
圈圈圆圆圈圈
计算 a + c + b = b + c + a
若无交集,则a + b = b + a
while(node1!=node2){
node1=node1==NULL?headB:node1->next;
node2=node2==NULL?headA:node2->next;
}
尾巴与另一个的头相连,while(不相等就走)
没相交的话最后都走到NULL也相等退出循环了!!
https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/comments/
解
https://leetcode.cn/problems/linked-list-cycle-ii/
二叉树-嚓嚓嚓
dfs-前-中-后-回溯
//前序,中左右
void preorderTraversal(TreeNode* root) {
if(root==nullptr)
return;
cout<<root->val;
preorderTraversal(root->left);
preorderTraversal(root->right);
}
//中序,左中右
void plan(TreeNode* root) {
if(root==nullptr)
return;
plan(root->left);
cout<<root->val;
plan(root->right);
}
//后序,左右中
void plan(TreeNode* root) {
if(root==nullptr)
return;
plan(root->left);
plan(root->right);
cout<<root->val;
}
dfs-前-中-后-迭代
//前序,中左右
void dfs(treeNode* root){
stack<treeNode*> s;
while(root!=nullptr||!s.empty()){
if(root==nullptr){
root=s.top()->right;
s.pop();
}
cout<<root->val;
s.push(root);
root=root->left;
}
}
//中序,左中右
void dfs(treeNode* root){
stack<treeNode*> s;
while(root!=nullptr||!s.empty()){
if(root==nullptr){
cout<<s.top()->val;
root=s.top()->right;
s.pop();
}
s.push(root);
root=root->left;
}
}
//后序,左右中
//先弄成中右左,再翻转一下
void dfs(treeNode* root){
stack<treeNode*> m_s;
vector<int> ans;
while(root!=nullptr||!m_s.empty()){
if(root==nullptr){
root=m_s.top()->left;
m_s.pop();
}
ans.push_back(root->val);
m_s.push(root);
root=root->right;
}
reverse(ans.begin(),ans.end());
}
层次法
void bfs(treeNode* root){
queue<treeNode*> m_q;
if(root!=nullptr)
m_q.push_back(root);
while(!m_q.empty()){
int numscount=m_q.size();
for(int i=0;i<numscount;i++){
treeNode* node=m_q.front();
m_q.pop_front();
cout<<node->val;
if(node->left!=nullptr)
m_q.push_back(node->left);
if(node->right!=nullptr)
m_q.push_back(node->right);
}
}
}
判断是否为搜索二叉树
重点 搜索二叉树的中序遍历为递增的
https://leetcode.cn/problems/validate-binary-search-tree/
回溯大法
模板
问题需要返回的类型0;
中间暂存量temp
void backtrack(原参数&1,int path){
if(path==1.size()){
0.push_back(temp);
return;
}
for(int i=path;i<1.size();i++){
temp.push_back(1[i]);
backtrack(1,i+1); //可重复为backtrack(1,i);
temp.pop_back(); //回退
}
}
如果问题当中有条件,可新建一个新函数放置判断是否符合条件
模板
问题需要返回的类型0;
中间暂存量temp
void backtrack(原参数&1,int path){
if(path==1.size()){
0.push_back(temp);
return;
}
for(int i=path;i<1.size();i++){
if(isval(1,path,i){
temp.push_back(1[i]);
backtrack(1,i+1); //可重复为backtrack(1,i);
temp.pop_back(); //回退
}
}
}
bool isval(原参数&1,int left,int right){
//判断流程
}
来个栗子,如分割回文字
class Solution {
public:
vector<vector<string>> ans;
vector<string> stemp;
vector<vector<string>> partition(string s) {
backtrack(s,0);
return ans;
}
void backtrack(string &s,int path){
if(path>=s.size())
{
ans.push_back(stemp);
return;
}
for(int i=path;i<s.size();i++){
if(ispars(s,path,i)){
stemp.push_back(s.substr(path, i - path + 1)) ;
backtrack(s,i+1);
stemp.pop_back();
}
}
}
bool ispars(string&s,int left,int right){
while(left<right){
if(s[left]!=s[right])
return false;
++left;
--right;
}
return true;
}
};
小tip,used数组
使用vectorused 来记录使用过的元素,达到去重的目的,举个例子:
全排列
public:
vector<vector<int>> ans;
vector<int> temp;
vector<vector<int>> permute(vector<int>& nums) {
vector<bool>used(nums.size(),false);
backtrack(nums,used);
return ans;
}
void backtrack(vector<int>&nums,vector<bool>&used){
if(temp.size()==nums.size()){
ans.push_back(temp);
return;
}
for(int i=0;i<nums.size();i++){
if(used[i])
continue;
temp.push_back(nums[i]);
used[i]=true;
backtrack(nums,used);
temp.pop_back();
used[i]=false;
}
}
};
贪心
重点:1.当有多个约束条件时,先考虑一边再考虑另一边,小心顾此失彼
例如:分发糖果 https://leetcode.cn/problems/candy/
2.找重合区间,先排序,再遍历,保留最大边界,合并。
动态规划
重点
1.01背包问题,好难的,背住吧
理论思想,dp[j]中放的为背包大小为 j 时的种类/价值,dp[j-nums[i]] 为找到能放下当前物品的最大背包,加上当前物品与不加进行比较,那个大要哪个,求种类的时候则都加上+=。
套路模板
//条件:一个负重n的背包,装质量为weight[],价值为val[]的石头,他们存放在nums[]里
vector<int> dp(n+1,0);
for(int i=0;i<nums.size();i++){
for(int j=n;j>weight[i];--j){
dp[i]=max(dp[i],dp[i-weight[i]]+val[i]);
}
}
//有时候weight[]、val[]和nums[]是一体的。
举个栗子,如分割子集 https://leetcode.cn/problems/partition-equal-subset-sum/
目的,找到总量一半的字符
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum=0;
for(int x:nums){
sum+=x;
}
if(sum%2!=0) return false;
vector<int> dp(sum/2+1,0);
for(int i=0;i<nums.size();i++){
for(int j=dp.size()-1;j>=nums[i];j--){
dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);
}
}
return dp.back()==sum/2;
}
};
理论思想,dp[j]中放的为背包大小为 j 时的种类/价值,dp[j-nums[i]] 为找到能放下当前物品的最大背包,加上当前物品与不加进行比较,那个大要哪个,求种类的时候则都加上+=。
其中找一组符合要求的用dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);
找所有符合要求的则要用dp[j]+=dp[j-nums[i]];
完全背包
背包中的东西可以重复选择,因为可以重复选所以遍历背包的时候不需要从后往前遍历,直接从前往后遍历就行,其中分为组合和排列两种问题
1.组合情况
https://leetcode.cn/problems/coin-change-ii/
class Solution {
public:
int change(int amount, vector<int>& coins) {
vector<int>dp(amount+1,0);
dp[0]=1;
for(int i=0;i<coins.size();i++){
for(int j=coins[i];j<dp.size();j++){//只需改动遍历背包的顺序
dp[j]+=dp[j-coins[i]];
}
}
return dp[amount];
}
};
2.排列,排列的时候[1,5]与[5,1]被认为是两种方法,所以先遍历背包再遍历物品
https://leetcode.cn/problems/combination-sum-iv/
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
vector<int>dp(target+1,0);
dp[0]=1;
for(int j=0;j<dp.size();j++){ //先遍历背包!!
for(int i=0;i<nums.size();i++){
if(j>=nums[i]&&dp[j]<INT_MAX-dp[j-nums[i]])//判断移出来
dp[j]+=dp[j-nums[i]];
}
}
return dp[target];
}
};
买卖股票
重点:状态描述
状态 | 第一天 | 第二天 |
---|---|---|
买了省多少钱 (买入态) | -prices[0] | 。。 |
卖了省多少钱 (卖出态) | 0 | 。。 |
啥也不干省多少钱 (就绪态) | 0 | 。。 |
(冷冻期省多少钱)(挂起态) | 0 | 。。 |
维护k个二维数组,buyk、sellk,分别代表k天的没买、买了、没卖、卖了,四种状态,
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
++k;
vector<int>buy(k, -prices[0]);
vector<int>sell(k, 0);
for (int i = 1 ; i < prices.size(); i++) {
for (int j = 1; j < k; j++) {
buy[j] = max(buy[j], sell[j-1] - prices[i]);
sell[j] = max(sell[j], buy[j] + prices[i]);
}
}
return sell.back() ;
}
};
k为二的话退化一下看着更清楚
(只能买卖两次)
class Solution {
public:
int maxProfit(vector<int>& prices) {
int buy1 = -prices[0], sell1 = 0;
int buy2 = -prices[0], sell2 = 0;
for (int i = 1; i < prices.size(); i++) {
buy1 = max(buy1, -prices[i]);
sell1 = max(sell1, buy1 + prices[i]);
buy2 = max(buy2, sell1 - prices[i]);
sell2 = max(sell2, buy2 + prices[i]);
}
return sell2;
}
};
连续的最长子串型
模板
连续的时候:
dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。
当A[i - 1] 和B[j - 1]相等的时候,dp[i][j] = dp[i - 1][j - 1] + 1;
记得要保存下最大值
不连续的时候再加一步:
不相等时, dp[i][j]=max( dp[i-1][j] , dp[i][j-1]);
注意:定义vector以及循环的时候为len+1
连续的例如:给两个数组求最大公共子串
https://leetcode.cn/problems/maximum-length-of-repeated-subarray/
int findLength(vector<int>& nums1, vector<int>& nums2) {
int len1=nums1.size(),len2=nums2.size();
int ans=0;
vector<vector<int>>dp(len1+1,vector<int>(len2+1,0));
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;j++){
if(nums1[i-1]==nums2[j-1])
dp[i][j]=dp[i-1][j-1]+1;
ans=ans>dp[i][j]?ans:dp[i][j];
}
}
return ans;
}
不连续的例如:最长公共子序列
https://leetcode.cn/problems/longest-common-subsequence/
int longestCommonSubsequence(string text1, string text2) {
int len1=text1.size(),len2=text2.size();
vector<vector<int>>dp(len1+1,vector<int>(len2+1,0));
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;j++){
if(text1[i-1]==text2[j-1])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
return dp[len1][len2];
}
最长相同子序列
给定两个字符串str1和str2,输出两个字符串的最长公共子序列。如果最长公共子序列为空,则返回"-1"。目前给出的数据,仅仅会存在一个最长的公共子序列
示例1
输入:
“1A2C3D4B56”,“B1D23A456A”
返回值:
“123456”
只用动态规划能算出最长的子序列长度,但还需要返回路径,则需要额外一个二维数组记录路径,最后拼接字符返回,定义二维数组时长度比字符多一位可以免去考虑字符越界的情况(如i-1>0),可以省去给边界赋值。
代码
using d_arry = vector<vector<int>>;
using o_arry = vector<int>;
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* longest common subsequence
* @param s1 string字符串 the string
* @param s2 string字符串 the string
* @return string字符串
*/
string LCS(string s1, string s2) {
int len1 = s1.size(), len2 = s2.size();
if(len1==0||len2==0)
return "-1";
d_arry dp(len1+1, o_arry(len2+1, 0));
d_arry path(len1+1, o_arry(len2+1, 0));
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;j++){
if(s1[i-1]==s2[j-1])
{
dp[i][j]=dp[i-1][j-1]+1;
path[i][j]=3;
}else{
if(dp[i-1][j]>dp[i][j-1]){
dp[i][j]=dp[i-1][j];
path[i][j]=1;
}else{
dp[i][j]=dp[i][j-1];
path[i][j]=2;
}
}
}
}
string ans;
int i=len1,j=len2;
if(dp[i][j]==0)
return "-1";
while(i>=0&&j>=0){
switch (path[i][j]) {
case 3:
ans+=s1[i-1];
i--;
j--;
break;
case 2:
j--;
break;
case 1:
i--;
break;
default:
i--;
j--;
}
}
reverse(ans.begin(),ans.end());
return ans;
}
};
回文字串
一个方形矩阵,dp[ i ][ j ] 代表 i 到 j 之间是否为回文串,
== 所以可由 dp[i+1][j-1] 来推断 dp [ i ][ j ];所以 i 从右往左遍历,j 从左往右遍历 ==
class Solution {
public:
int countSubstrings(string s) {
int res=0;
vector<vector<bool>>dp(s.size(),vector<bool>(s.size(),false));
for(int i=s.size()-1;i>=0;i--){
for(int j=i;j<s.size();j++){
if(s[i]==s[j]){
if(j-i<=1||dp[i+1][j-1]){
res++;
dp[i][j]=true;
}
}
}
}
return res;
}
};
如果要算最长回文子序列
将矩阵bool改为int,计算i 到 j 之间最长的字串
class Solution {
public:
int longestPalindromeSubseq(string s) {
int len=s.size();
vector<vector<int>> dp(len,vector<int>(len,1));
for(int i=len-1;i>=0;i--){
for(int j=i+1;j<len;j++){
if(s[i]==s[j]){
if(j-i==1){
dp[i][j]=2;
}else{
dp[i][j]=dp[i+1][j-1]+2;
}
}else
dp[i][j] = max(dp[i][j - 1],dp[i+1][j]);
}
}
return dp[0][len-1];
}
};
回文子串(连续的)
int getLongestPalindrome(string A) {
vector<vector<int>>dp(A.size(),vector<int>(A.size(),0));
int ans=0;
for(int i=A.size()-1;i>=0;i--){
for(int j=i;j<A.size();j++){
if(A[i]==A[j]){
if (j==i) //它自身
dp[i][j]=1;
else if(j-i==1) //左右俩
dp[i][j]=2;
else if(dp[i+1][j-1]!=0)//注意这个判断条件,中间不是回文两头相同也没用
dp[i][j]=dp[i+1][j-1]+2;
else
dp[i][j]=0;
ans=max(ans,dp[i][j]);
}
else {
dp[i][j]=0;
}
}
}
return ans;
}
编辑距离
两个字符串进行匹配,可以通过这种方法计算相似度
https://leetcode.cn/problems/edit-distance/
初始化 i=0 时无法匹配 j 必须操作他字符长度的量 dp[0][j]=j;
i 同理!
if (word1[i - 1] == word2[j - 1]) | if (word1[i - 1] != word2[j - 1]) |
---|---|
不操作 | 增 |
删 | |
换 |
int minDistance(string word1, string word2) {
int len1=word1.size(),len2=word2.size();
vector<vector<int>> dp(len1+1,vector<int>(len2+1,0));
for(int i=0;i<=len1;i++) dp[i][0]=i;
for(int j=0;j<=len2;j++) dp[0][j]=j;
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;j++){
if(word2[j-1]==word1[i-1])
dp[i][j]=dp[i-1][j-1];
else{
dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;
//替换 删i 删j 添加和删除同操作
}
}
}
return dp[len1][len2];
}
秋招笔试遇到的值得看到题
米哈游
世界树题
第一个节点深度为1
根据深度*节点值求和取最小,可以改动一个边
输入
第一行输入所有节点个数n
第二行输入n个整数表明节点值大小
剩下的n-1行输入边
如
4
1,2,3,10
1,2
2,3
3,4
示例
1*1+2*2+3*3+4*10;把4节点接到1处
得1*1+(10+2)*2+3*3最小
代码
#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<vector>
#include<set>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
int n = 4;
vector<int> deep(n + 1);
vector<ll> score(n + 1), sum(n + 1);
void dfs(vector<int>&a, vector<vector<int>> &e,int u, int depth) {
deep[u] = depth;
score[u] = 1LL * depth * a[u];
sum[u] = a[u];
for (auto&& v : e[u]) {
dfs(a,e,v, depth + 1);
score[u] += score[v];
sum[u] += sum[v];
}
}
void dfs2(vector<vector<int>>& e,int u, ll &ans) {
if (deep[u] > 2 && score[1] - sum[u] * (deep[u] - 2) < ans) {
ans = score[1] - sum[u] * (deep[u] - 2);
}
for (auto&& v : e[u]) {
dfs2(e,v,ans);
}
}
int main() {
vector<int> a={0,1,2,3,10};
vector<vector<int>> e = { {},{2},{3},{4},{} };
dfs(a,e,1, 1);
ll ans = score[1];
dfs2(e,1, ans);
cout << ans << '\n';
return 0;
}
京东
第二题,
输入一个数组{1,2,3,4};
可以对其将后两位进行加或乘操作,然后所得数取个位值再加入到数组中,求数组只剩最后一个数时,是几的数量。
用层次遍历的思想,总遍历nums.size()-1次,然后层里从后往前遍历,看看加或乘之后的数与之前已经进行过操作的数比较,要是一样本次不操作,让之前那个的nums值加本次的值。这样下去每层的大小最多为10。
代码,考虑只有一个数且为10,输出0-1
#include<iostream>
#include<string>
#include<vector>
#include<set>
#include<queue>
using namespace std;
struct arr {
vector<int>a;
int nums = 0;
};
int main() {
//int n = 0;
//cin >> n;
//vector<int>nums(n, 0);
//for (int i = 0; i < n; i++) {
// cin >> nums[i];
//}
vector<int>nums = { 1,2,3,4 };
int n = nums.size();
if(n==1&&nums[0]==10){ //特殊情况进行处理
count<<"0-1"<<endl;
reutrn;
}
vector<arr>que;
arr temp = { nums,1 }; //初试数组放进去
que.push_back(temp);
for (int i = n; i > 1; i--) {
int count = que.size(); //当前层的大小
vector<arr>temp_que; //下一层临时存储
for (int i = count-1; i >=0; i--) { //遍历,从后往前从前往后都行
vector<int>temp = que[i].a;
int temp1 = temp.back(); //拿出后两个值
temp.pop_back();
int temp2 = temp.back();
temp.pop_back();
int temp3 = (temp1 + temp2) % 10; //加运算
int temp4 = (temp1 * temp2) % 10; //乘运算
bool is3 = false; //加后的是否有重复的
bool is4 = false; //乘后的是否有重复的
for (int j = 0; j < temp_que.size(); j++) { //遍历已操作的数组,去重本次
if (temp_que[j].a.back() == temp3 && temp3 == temp4) {
temp_que[j].nums += 2*que[i].nums;
is3 = true;
is4 = true;
break;
}
else if (temp_que[j].a.back() == temp3&&!is3) {
temp_que[j].nums += que[i].nums;
is3 = true;
}
else if (temp_que[j].a.back() == temp4&&!is4) {
temp_que[j].nums += que[i].nums;
is4 = true;
}
}
if (!is3) {
temp.push_back(temp3);
temp_que.push_back({ temp,que[i].nums });
temp.pop_back();
}
if (!is4) {
temp.push_back(temp4);
temp_que.push_back({ temp,que[i].nums });
}
}
que = temp_que; //替换本层数组
}
for (auto x : que) {
cout << x.a[0] << "-" << x.nums << endl;
}
}
美团
第3题
找总值,长度想等,每位都不同的组合数量,每位最大为300,最小为1;
栗子:
输入
1,1,3
输出
1----------只有2,2,1这一种可能
使用回溯+记忆数组的思路(今年考好几个这种题了)
dp[i][j] 其中i为已经填补的位的数量,j为已填补的位上数的和,dp[i][j]为剩下没填补位一共有几种可能。
直接上代码
#include <cstdlib>
#include <iostream>
#include<vector>
using namespace std;
int ans = 0;
int summ(vector<int>& path) {//vector求和函数
int sum_path = 0;
for (int i = 0; i < path.size(); i++) {
sum_path += path[i];
}
return sum_path;
}
void track(vector<int>& nums, int sum, vector<int>& path, vector<vector<int>>& dp) {
int len1 = nums.size(), len2 = path.size();//拿到已填充的长度和整体长度
int temp_sum = summ(path); //拿到已填充位之和
if (len2 == len1 - 1) { //如果只剩最后一位,可能的结果要不为1要不为0
if (sum - temp_sum != nums.back()) //题中要求同位数字不能一样
dp[len2][temp_sum] = 1;
else
dp[len2][temp_sum] = 0;
return;
}
if (dp[len2][temp_sum] != -1) { //记忆数组的重点,当前dp不为-1说明已经计算过,
return; //不进行继续计算
}
int temp = 0; //计算本轮可组合的个数
int begin = temp_sum - 300 * (len1 - len2 - 1); //剪枝方法,
int end = sum - temp_sum - len1 + len2 + 1; //剪枝方法,
for (int i = 1 > begin ? 1 : begin; i <= end; ++i) {
if (nums[len2] == i) //题中要求的条件
continue;
path.push_back(i); //可填上当前位
track(nums, sum, path, dp);
temp+= dp[len2+1][summ(path)];
path.pop_back(); //回退
}
dp[len2][temp_sum] = temp; //记录记忆数组
}
int main() {
int count = 3;
vector<int>nums={1,1,3};
vector<int>path;
int sum = 0;
for (int i = 0; i < count; ++i) {
sum += nums[i];
}
vector<vector<int>>dp(count, vector<int>(sum, -1));
track(nums, sum, path, dp);
cout << dp[0][0] << endl; //啥也不填计算要的结果
}
// 64 位输出请用 printf("%lld")
快手面试题
重点:观察题目,如果手牌中某种花色<3,一定只能凑同花,所以先紧着同花整,整不出来就肯定凑不成手牌,所有花色数量<3的牌都凑成同花再凑顺子,顺子,相当于连续数组。
上代码
#include<iostream>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;
int main() {
vector<int>nums({ 101,103,204,104,102,304 });//性感荷官在线发的牌
vector<bool>used(nums.size(), false); //用来标记当前手牌有没有凑成对
sort(nums.begin(), nums.end()); //先理下牌
map<int,int>colors; //记录手牌花色的数量
vector<vector<int>>ans;
for (int i = 0; i < nums.size(); i++) {
colors[nums[i] / 100]++;
}
for (auto x : colors) { //先匹配同花,找到花色数量<3的花色
if (x.second < 3) {
int color = x.first;
for (int i = 0; i < nums.size(); i++) {
if (used[i])
continue;
if (nums[i] / 100 == color) {
int num = nums[i] % 100; //找到花色数量<3的数字
vector<int>temp;
temp.push_back(nums[i]);
used[i] = true;
for (int j = 0; j < nums.size(); j++) {
if (used[j])
continue; //找到花色数量<3的数字相同且花色不同的牌
if (num == nums[j] % 100 && color != nums[j] / 100) {
temp.push_back(nums[j]);
used[j] = true;
}
}
if (temp.size() < 3) //凑不到3张,退出,返回-1;
{
cout << -1;
return 0;
}
ans.push_back(temp); //凑到了,压入答案里
}
}
}
}
vector<int>temp;
for (int i = 0; i < nums.size(); i++) { //开始找顺子
if (used[i])
continue;
if (temp.empty() || temp.back() == nums[i] - 1) //排序了,所以直接找连续数
{
temp.push_back(nums[i]);
used[i] = true;
}
else {
if (temp.size() < 3) //凑不到3张,退出,返回-1;
{
cout << -1;
return 0;
}
ans.push_back(temp);
temp.clear();
}
}
if (!temp.empty()) //最后一组压入答案
ans.push_back(temp);
for (auto x : used) { //判断是否所有手牌都用了
if (!x) {
cout << -1;
return 0;
}
}
for (auto x : ans) { //打印输出结果
for (auto x1 : x) {
cout << x1 << " ";
}
cout << endl;
}
}
广联达笔试
懒得构建树了,直接拿map查找了,其实用int也行,范围应该不会超。先找叶子,叶子为1,然后往上找节点。
#include<iostream>
#include<map>
#include<vector>
using namespace std;
int plan(map<long long, vector<long long>>& a, long long b) {
int res = 0;
if (a.find(b) == a.end()) //map里没有说明是叶子节点返回1;
return 1;
else { //map里有说明是节点继续找叶子节点;
vector<long long> temp = a[b];
for (int i = 0; i < temp.size(); i++) {
res += plan(a, temp[i]);
}
}
return res;
}
int main() {
long long n = 0, m = 0, q = 0;
cin >> n >> m >> q;
map<long long, vector<long long>>a; //key为父节点,val为vector数组,存它的孩子们
vector<vector<long long>>num(2, vector<long long>(m, 0)); //专门用来接受输入的
vector<long long>ques(q, 0); //结果要的路子
for (int i = 0; i < 2; i++) {
for (int j = 0; j < m; j++) {
cin >> num[i][j];
}
}
for (int j = 0; j < q; j++) {
cin >> ques[j];
}
for (int j = 0; j < m; j++) {
a[num[0][j]].push_back(num[1][j]); //吧路径塞到map里
}
vector<long long>dp(n + 1, 0); //dp放每个节点的路子
for (int i = n; i > 0; i--) {
if (a.find(i) == a.end()) { //map里没有,说明为叶子节点直接放1
dp[i] = 1;
}else{ //map里有,说明为节点找它儿子看看
vector<long long>temp = a[i];
for (int j = 0; j < temp.size(); j++) {
dp[i] += plan(a, temp[j]); //把它儿子的路都加起来给他
}
}
}
for (int i = 0; i < ques.size(); i++) {
cout << dp[ques[i]] << " ";
}
}
掌阅科技笔试
第三题题目
机器人从起始点走到目标点最短的步数
。证明能走 s为起始点 #不能走 e为终点
输入
3 3
。# e
。# 。
s 。 。
使用广度优先遍历,将下一步的路径点与步数压入队列,同时遍历后将map改为0,不再走了
int main() {
int n, m;
cin >> n >> m;
vector<vector<char>>map(n, vector<char>(m));
int a, b;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> map[i][j];
if (map[i][j] == 's') {
a = i;
b = j;
}
}
}
queue<int>m_q;
m_q.push(a);//压x
m_q.push(b);//压y
int i = 0;
m_q.push(i);//压步数
while (!m_q.empty()) {
a = m_q.front();//弹x
m_q.pop();
b = m_q.front();//弹y
m_q.pop();
i = m_q.front();//弹步数
m_q.pop();
if (map[a][b] == 'e') {
cout << i; //找到终点,输出当前步数
return 0;
}
else if (map[a][b] == '.' || map[a][b] == 's') {
map[a][b] = '0'; //减少重复遍历
//下面四组遍历条件,同时压队列
if (a + 1 < map.size()) {
m_q.push(a + 1);
m_q.push(b);
m_q.push(i + 1);
}
if (b + 1 < map[a].size())
{
m_q.push(a);
m_q.push(b + 1);
m_q.push(i + 1);
}
if (a - 1 >= 0)
{
m_q.push(a - 1);
m_q.push(b);
m_q.push(i + 1);
}
if (b - 1 >= 0)
{
m_q.push(a);
m_q.push(b - 1);
m_q.push(i + 1);
}
}
}
cout << -1;
}