力扣

前提

vscode默认不支持c++11解决办法

vscode C++ 简易配置(支持c++11以上) - 知乎 (zhihu.com)

单行输入,过滤逗号

1
2
3
4
5
6
7
8
9
vector<int> s;
int temp;
while(cin>>temp)
{
s.emplace_back(temp); //可以过滤逗号
if(cin.get()=='\n') break;
}
for(int i:s)
cout<<i<<" ";

多行输入,外部以回车分割

内部如果是空格分割

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  int n,m;

cin>>n>>m;

getchar();//取走回车,为了下面的getline

vector<string>sw;

for(int i=0;i<m;i++)

{

string s,tmp;

getline(cin,s);

istringstream ss(s);

while(ss>>tmp){

sw.push_back(tmp);

}

}

for(string i:sw)

cout<<i<<" ";

1
2
1 1 2 3
4 5 6 7
1 2 3 5 6 7 sw的元素个数为6

1
while(getline(ss,tmp,',')) //逗号分割

3
2
1,2,3,4,5,6
7,8,9,10,11
1 2 3 4 5 6 7 8 9 10 11 sw的总数为11

外行输入,多个数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
vector<string> sw;

string s;

int n;

cin>>n;

getchar(); //否则,n=2时,下文的getline只读一行,因为之前的回车读到了

// cin.ignore();

for(int i=0;i<n;i++)

{

getline(cin,s);//默认分隔符为'\n' cin会默认去除\n

sw.push_back(s);

}

2
1,2
5,7
1,2 5,7

我再用string转化一波吧

还有另一个做法

1
2
3
4
5
6
7
8
9
10
vector<string> sw(2,"");
string s;
for(int i=0;i<2;i++)
{

getline(cin,s);
sw[i]=s;//如果是vector<string> sw;那就用push_back,因为这样定义是没有索引的。
}
for(string i:sw)
cout<<i<<" ";

1,2,3
4,5
1,2,3 4,5

1
2
3
...
getline(cin,sw[i]);也可以
...

基于struct的存储

struct book{

int a;

int b;

};

vector<book> a(n);

关于每行两元素的存储

以下根据实际情况等价替代

map<int,int> mp;

vector<pair<int,int> > a;

pair<int,int> p=make_pair(3,4);

vector<vector<int> > a;

vector<book> a;

基本框架

数据结构的存储方式:数组和链表

数据结构的基本操作:遍历+访问

线性就是for/while迭代,否则就是递归(二叉树)

线性表略

链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class listnode{
int val;
listnode next;
}
void traverse(listnode head)
{
for(listnode p=head;p!=null;p=p->next)
{
//迭代访问p.val
}
//或者递归访问head.val
traverse(head->next)

}

二叉树

1
2
3
4
5
6
7
8
9
class Treenode{
int val;
Treenode left,right;
}
void traverse(Treenode root)
{
traverse(root.left);
traverse(root.right);
}

N叉树

1
2
3
4
5
6
7
8
9
class Treenode{
int val;
Treenode[] children;
}
void traverse(Treenode root)
{
for(Treenode child:root.children)
traverse(child);
}

进而扩展为图,图就相当于N叉树的结合体

二叉树

二叉树基本遍历比如

travers(root.left);//一直往左遍历,直到root为null,会跳到下一行

travers(root.right);//一直往右遍历,直到root为null,会跳到下一行,若是递归,则会再从该行开始(一般是先左子树,左子树遍历完,轮到右子树)

单链表的倒序问题

1
2
3
4
5
6
7
8
9
void travers(treenode * head)
{
if(head==nullptr) return;
travers(head->next);
cout<<head->val;

//实现5->4->3->2->1

}

递归就相当于个栈,递归终止之后,就不停的pop栈顶元素

当递归函数调用结束后,系统会负责将函数的返回值从栈中取出,并将控制权返回给调用该函数的地方。系统会将返回值传递给上一层的递归调用,使得上一层递归函数可以继续执行后续的操作。

二叉树问题,分为回溯法(一遍二叉树遍历)和动态规划(分解问题,有返回值)

避坑点

思考整棵树与其左右字数关系,类似数学归纳法。

当程序”递“时,计算机栈保存该对象,程序不断”递“,计算机不断压栈;边界时,程序不断发生”归“的动作时,计算机不断出栈。

104二叉树最大深度

遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
int depth=0;
int res=0;//记录最大值

int maxDepth(TreeNode* root) { //单纯访问
travers(root);
return res;
}
void travers(TreeNode* root)
{
if(root==nullptr) return;
depth++;
res=max(depth,res);
travers(root->left);
travers(root->right);
depth--;
}
};
//将depth理解为在二叉树上游走的指针即可

递归,遇见终止的,就释放,回到原位置

分解

image-20230711221213896

原问题:计算3为根节点的二叉树的深度。
子问题:计算9为根节点的二叉树的深度。
子问题:计算20为根节点的二叉树的深度。
所谓子问题就是和原问题一样,只是规模降低了。

当前节点深度=max(左节点深度,右节点深度)+1

1
2
3
4
5
6
7
8
int getdepth(TreeNode *root)
{
if(root==nullptr) return 0; //叶子结点
int leftmax=getdepth(root->left);
int rightmax=getdepth(root->right);

return 1+max(leftmax,rightmax);
}

144 二叉树前序遍历

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

回溯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
vector<int> s;
vector<int> preorderTraversal(TreeNode* root) {
travers(root);
return s;
}
void travers(TreeNode *root)
{
if(root==nullptr) return;

s.push_back(root->val);
travers(root->left);
travers(root->right);
}
};

分解

聚焦于子问题,类似于数学归纳法,子问题的逻辑正确,整体问题一定解决。

前序遍历就是根节点+左子树遍历结果+右子树遍历结果

1
2
3
4
5
6
7
8
9
10
11
12
13
vector<int> preorderTraversal(TreeNode* root) {
vector<int> s;//根节点,左子树,右子树
if(root==nullptr) return s;

s.push_back(root->val);
vector<int> left=preorderTraversal(root->left);
s.insert(s.end(),left.begin(),left.end());
vector<int> right=preorderTraversal(root->right);
s.insert(s.end(),right.begin(),right.end());

return s;

}

后序位置的妙用

1.把根节点当做第1层,打印出每个节点所在层数 (前序)

1
2
3
4
5
6
7
8
9
void traverse(TreeNode* root,int level)
{
if(root==nullptr) return;
printf("节点%s在第%d层",root,level);
traverse(root->left,level+1);
traverse(root->right,level+1);
}
//调用
traverse(root,1);

2.打印每个结点的左右子树各有多少结点 (后序)

1
2
3
4
5
6
7
8
9
int count(TreeNode* root){
if(root==nullptr) return 0;
int leftco=count(root->left);
int rightco=count(toor->right);
//后序位置
printf(...)

return leftco+rightco+1;
}

这两个问题区别在于,第一个是递归时顺便带来的结果;第二个需要遍历完整棵树,才知道的结果

543二叉树的直径

给你一棵二叉树的根节点,返回该树的 直径 。

二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。

两节点之间路径的 长度 由它们之间边数表示。

image-20230712101353602

输入:root = [1,2,3,4,5]
输出:3
解释:3 ,取路径 [4,2,1,3] 或 [5,2,1,3] 的长度。
示例 2:

输入:root = [1,2]
输出:1

思想

长度=左子树深度+右子树深度

所以求depth(1),必求depth(2)和depth(3)

求depth(2),必求depth(4)和depth(5)

递归思想是先解决子问题,再回到整体问题。

以下代码所示(注意,不正确)

1
2
3
4
5
6
7
8
9
10
int depth(TreeNode * root)
{
if(root==nullptr) return 0;
int left=depth(root->left);
int right=depth(root->right);

return left+right+1;
}
...
//调用该函数depth(root)

int left=depth(root->left);当root为4时,执行该行

好,从头执行depth(左孩子),4的左孩子是空指针,返回值得到0,赋值left=0

进入下一行

int right=depth(root->right);进入该行

从头执行depth(右孩子),4的右孩子是空指针,返回值得到0,赋值right=0

进入下一行

return left+right+1;进入该行。

得到depth(4)的返回值为1。

同理depth(5)的返回值为1。

则depth(2)的长度为2。

当然,少考虑了一种情况,如果不经过根节点嘞,即只有左子树或只有右子树情况

image-20230712103553998

这个是时候可以一眼看出,直径为3,而不是2了吧。

这就需要取左子树和右子树深度的最大值了

1
2
3
4
5
6
7
8
9
10
11
12
13
int res=0;
...
int depth(TreeNode * root)
{
if(root==nullptr) return 0;
int left=depth(root->left);
int right=depth(root->right);
//后序位置
res=max(left+right,res);
return max(left,right)+1;
}
...
//调用

遇到子树问题,首先想到的是给函数设置返回值,然后在后序位置做文章。

动态规划/回溯/DFS

动态规划例子

计算二叉树共有多少个节点

着眼结构相同的子类问题

1
2
3
4
5
6
7
8
int count(TreeNode * root)
{
if(root==nullptr) return 0;
int left=count(root->left);
int right=count(root->right);

return left+right+1;
}

斐波那契

1
2
3
4
5
int fib(int N)
{
if(N==1||N==2) return 1;//递归出口
return fib(N-1)+fib(N-2);
}

回溯

遍历的思路,traverse(),打印出遍历二叉树的过程

1
2
3
4
5
6
7
8
9
10
11
void travers(TreeNode * root)
{
if(root==nullptr) return;
printf("从结点%s到%s",root,root->left);
travers(root->left);
printf("从结点%s回到%s",root->left,root);

printf("从结点%s到%s",root,root->right);
travers(root->right);
printf("从结点%s回到%s",root->right,root);
}

多叉树遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Node{
public:
int val;
vector<Node*> children;
};
void travers(Node *root)
{
if(root==nullptr) return;
for(Node * child=root;root->children!=nullptr;root=root->children)
{
printf("从结点%s到%s",root,child);
travers(child);
printf("从结点%s回到%s",child,root);
}
}

多叉树的遍历框架可以延伸到回溯法

着眼节点之间的移动

1
2
3
4
5
6
7
8
9
10
11
12
void backtrack()
{
for(int i=0;i<...,i++)
{
//做选择
...
//进入下一层决策树
backtrack(...);

//撤销刚才做的选择
}
}

DFS

深度遍历

travers函数使得二叉树每个结点值+1

着眼于单个结点上的操作

1
2
3
4
5
6
7
void traverse(TreeNode* root)
{
if(root==nullptr) return;
root->val+=1;
traverse(root.left);
traverse(root.right);
}

DFS与回溯区别

1
2
3
4
5
6
7
8
9
10
11
void dfs(TreeNode* root)
{
if(root==nullptr) return;
print("结点%s来了",root);
/*for (vector<Node*>::iterator it = root->children.begin(); it != root->children.end(); ++it)*/
for(Node* child:root->children)
{
dfs(child);
}
print("结点%s走了",root);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
void traverse(TreeNode *root)

{

if(root==nullptr) return;
for(...)
{
print(...root,child);
traverse(child);
print(...child,root);
}

}

回溯算法,必须把“做选择”和”撤销操作”放在循环里

层序遍历

//已知一棵树的根节点,层序遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void levelbianli(TreeNode* root)
{
if(root==nullptr) return;
queue<TreeNode*> q;
q.push(root);
//从上到下
while(!q.empty())
{
int index=q.size();
//从左到右
for(int i=0;i<index;i++)
{
TreeNode* cur=q.front();
q.pop();
//将下一层结点放入队列
if(cur->left!=nullptr)
q.push(cur->left);
if(cur->right!=nullptr)
q.push(cur->right);
}
}
}

二叉树输入

(搞了半天我这是前序接收输入,我说为什么后续遍历不对)

我定义了一个vector,提前接收字符串序列,如a,b,c,d;如果要转换成数值(求和)也是可以的,stoi转换一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
vector<string> create()
{
string ss;
cin>>ss;
string token;
istringstream iss(ss);
vector<string> list;
while(getline(iss,token,','))
{
list.push_back(token);
}
iss.clear();

return list;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void receive(TreeNode* &root,vector<string> ss)//前序接收输入
{
if(index<ss.size())
{
string tem=ss[index++];
// ss.pop_back();
if(tem=="nullptr")
{
root=nullptr;
return;
}
else
{
root=new TreeNode;
root->val=tem;
receive(root->left,ss);
receive(root->right,ss);
}
}
}

层序接收输入

#include<queue>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
void receive(TreeNode* &root,vector<string>& ss)
{

if(ss.empty())
{
root=nullptr;
return;
}

root=new TreeNode(ss[0]);//初始化,并添加第一个成员
ss.erase(ss.begin());//去除前端

queue <TreeNode*>nodequeue;
nodequeue.push(root);//进入第一个元素

while(!ss.empty())
{
TreeNode* cur=nodequeue.front();//访问前端元素
nodequeue.pop();//删除前端元素

//左孩子
string leftval=ss[0];//由于每次有erase的缘故,相当于访问下一个
ss.erase(ss.begin());//ss又减1
if(leftval!="nullptr")
{
cur->left=new TreeNode(leftval);
nodequeue.push(cur->left);
}
if(ss.empty()) break;

//右孩子

string rightval=ss[0];
ss.erase(ss.begin());
if(rightval!="nullptr")
{
cur->right=new TreeNode(rightval);
nodequeue.push(cur->right);
}
}

}

vector<int>版本的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
struct tree{

int val;
tree *left,*right;
// tree():val(),left(nullptr),right(nullptr){}
tree(int x):val(x),left(nullptr),right(nullptr){}
};
int start=0; //会导致输出有多余的0
void jieshou(vector<int> a,tree* &root)
{
if(start==a.size())
{
root=nullptr;
return;
}

queue<tree*> q;
root=new tree(a[start]);

q.push(root);

while(!q.empty())
{

tree *cur=q.front();
q.pop();

start++;
//左孩子
// if(a[start]!=-1) 不判断start的h话,会打印出0
if(start<a.size()&&a[start]!=-1)
{
cur->left=new tree(a[start]);
q.push(cur->left);
}
if(start==a.size()) break;

//右孩子
start++;
// if(a[start]!=-1)
if(start<a.size()&&a[start]!=-1)
{
cur->right=new tree(a[start]);
q.push(cur->right);
}

}

}
void bianli(tree* root)
{
if(root==nullptr) return;

cout<<root->val;
bianli(root->left);
bianli(root->right);
}
int main()
{
tree* node=nullptr;
vector<int> a{1,2,3,4,5,6,-1};
jieshou(a,node);
bianli(node);
//由于全局变量start=7超过索引,会打印0;1240536
cout<<endl<<start;
}

剑指55二叉树的深度

同104

226 反转二叉树

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

image-20230712200429292

1
2
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]

个人做法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
//即从第二层开始,倒序存储
//子问题,左孩子与右孩子换位置的问题
public:
void back(TreeNode* &root)
{
if(root==nullptr) return;
back(root->left);

back(root->right);

TreeNode *node=root->left;
root->left=root->right;
root->right=node;
}
TreeNode* invertTree(TreeNode* root) {
back(root);
return root;
}
};

分析

哎,还是很晕,不想细节了。

我想到的就是说孩子互换。但是按正常逻辑来说

遍历

单独抽出个结点,让它做什么,交换它的左右孩子

前中后位置都可以诶,中序要特别处理一下

分解

给函数一个定义,让它逻辑自洽

1
2
3
4
5
6
7
8
9
10
11
12
TreeNode* invertTree(TreeNode* root) {
if(root==nullptr) return nullptr;
//函数定义,反转左子树,但实际是记录吧?
TreeNode *left=invertTree(root->left);
TreeNode *right=invertTree(root->right);//记录了右节点

//交换左右节点
root->right=left;
root->left=right;

return root;
}

嘶,应该是这样吧

*116 填充结点的右侧指针

给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。

1
2
3
4
5
6
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

初始状态下,所有 next 指针都被设置为 NULL。

image-20230712220310325

遍历(模拟三叉树)

遍历,每个结点指向右侧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void bianli(Node* &root1,Node* &root2)//模拟三叉树
{
if(root1==nullptr||root2==nullptr)
{
return;
}
root1->next=root2;
//父节点相同的
bianli(root1->left,root1->right);
bianli(root2->left,root2->right);
//不同父节点的
bianli(root1->right,root2->left);

}

class Solution {
public:
Node* connect(Node* root) {
if(root==nullptr) return nullptr;
bianli(root->left,root->right);
return root;
}
};

它通过抽象一个三叉树

用左边的结点指向右边的结点

串联有三种情况:

左子树间的孩子串联

右子树间的孩子串联

左子树的最右孩子和右子树的最左孩子连接

输入为根结点的左孩子和右孩子

(哎,已经默认所有next指针设为null,就不需要特别考虑设置null了)

传统的那个遍历方式,就是少考虑了中间5->6的指向

分解不可以

思想

同父节点的连接

不同父节点的连接

总结

二叉树问题

1.遍历

2.子问题

3.对于单独结点,需要做的事;在什么位置做这件事。(其他结点会按照该逻辑重复做下去)

链表

合并有序链表

目的:将两个链表按升序排列

1.虚拟节点的建立 :ListNode* dumpy=new ListNode(-1);

  1. 访问该新链表:ListNode* p1=dumpy;
  2. 访问原链表: ListNode *p=head;
  3. 双指针思想

单链表分解

目的:将一个链表分解成两个链表

1.分别新建两个虚拟节点

2.分别指向该新虚拟节点

3.访问原链表

4.按条件做

合并k个有序链表

题目:给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

目的,合并k个链表(如何获取k个链表的最小节点)

引入:优先级队列(二叉堆)

1
priority_queue<ListNode*,vector<ListNode*>,function<bool(ListNode*,ListNode*)> >pq([](ListNode *a,ListNode* b){return a->val>b->val});

<参数类型,容器类型,比较函数>

或者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct cmp{
bool operator()(ListNode *&a,ListNode *&b)
{
return a->val>b->val;
}
};
...
priority_queue<ListNode*,vector<ListNode*>,cmp>pq;
priority_queue要求接受函数对象而不是函数指针

函数对象,如上述struct重构operator
而函数指针指的是:如
bool cmp(const int a, const int b) {
return a < b;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
struct cmp{
bool operator()(ListNode *&a,ListNode *&b)
{
return a->val>b->val;
}
};
ListNode* mergeKLists(vector<ListNode*>& lists) {

if(lists.empty()) return nullptr;
ListNode* dumpy=new ListNode(-1);//建立新链表
ListNode* p=dumpy;

priority_queue<ListNode*,vector<ListNode*>,cmp>pq;
for(ListNode* list:lists)
{
if(list!=nullptr)
pq.push(list);//存各链表中最小的结点
}
/*如lists = [[1,4,5],[1,3,4],[2,6]],pq存的是1->1>2

*/
while(!pq.empty())
{
ListNode *node=pq.top();//访问
pq.pop();//弹出
p->next=node;//接到p后面

if(node->next!=nullptr)//遍历某链表的下一个结点
{
pq.push(node->next);
}

p=p->next;//下一个指针

}
return dumpy->next;
}
};

if(node->next!=nullptr)//遍历某链表的下一个结点
{
pq.push(node->next);//加入非空节点入优先队列(会经过排序)
}
pq中值为1->1->2

一开始是1,是第一个链表的[1,4,5],
​ 后面的是4,加入到pq,则是1->2->4
然后访问1,是第二个链表的[1,3,4],
​ 后面的是3,加入到pq,则是2->3->4
然后访问2,是第三个链表的[2,6],
​ 后面的是6,加入到pq,则是3->4->6
然后访问3,第二链表的[1,3,4],
​ 后面是4,加入到pq,则是4->4->6
然后访问4,第一链表的[1,4,5],
​ 后面的是5,加入到pq,则是4->5->6
然后访问4,第二链表的[1,3,4]
​ 后面的是nullptr,终于不加入了,pq为5->6
然后访问5,第一链表的[1,4,5]
​ 后面的是nullptr,pq为6
然后访问6,没啦。

回溯

回溯,核心就是递归,在递归调用前选择,递归调用后撤销

通过列表中每固定一个数(做出的选择),形成了路径

纯暴力解法

1.列表中选择某点

#做选择

2.列表中移除该点

3.路径添加该点

4.递归

#撤销

5.路径移除该点

6.该选择恢复到列表中

全排列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
vector<vector<int> >res;
void backtrack(vector<int> a,vector<int> path,vector<bool>used);
int main()
{
vector<int> a;
int num;
while(cin>>num)
{
a.push_back(num);
if(cin.get()=='\n') break;
}
vector<int> path;//路径
vector<bool> used(a.size());
backtrack(a,path,used);

// for(auto li:res)
// {
// for(auto lii:li)
// cout<<lii;

// cout<<endl;
// }
}

void backtrack(vector<int> a,vector<int> path,vector<bool>used)
{
//触发结束条件
if(path.size()==a.size())
{
res.push_back(path);
return;
}
for(int i=0;i<a.size();i++){
//排除不合法选项
if(used[i]) //已经访问过
continue;
//选择
// cout<<"xuanze:"<<a[i]<<endl;
path.push_back(a[i]);
used[i]=true;
//做下一个选择
backtrack(a,path,used);
//撤销选择
path.pop_back();
used[i]=false;
}
}

N皇后

n*n的棋盘,放n个皇后,每行放一个,不得互相攻击,求可能的放法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
vector<vector<string> >res;
bool isvalid(vector<string> &board,int row,int col);
void backtrack(vector<string> a,int row);
int main()
{
int n;
cin>>n;
vector<string> board(n,string(n,'.'));//棋盘

backtrack(board,0);//从第0行开始放

for(auto li:res)
{
for(auto lii:li)
{
cout<<lii<<endl;
}


cout<<endl;
}
}

void backtrack(vector<string> a,int row)
{
//触发结束条件
if(row==a.size())
{
res.push_back(a);
return;
}
int n=a[row].size();
for(int col=0;col<n;col++){
//排除不合法选项

if(!isvalid(a,row,col))
{
continue;
}
//选择
a[row][col]='Q';
//做下一个选择
backtrack(a,row+1);

//撤销选择
a[row][col]='.';
}
}
bool isvalid(vector<string> &board,int row,int col)
{
int n=board.size();//行
//两行之间垂直,遇到左上,遇到右上
for(int i=0;i<=row;i++)//列
{
if(board[i][col]=='Q') return false;
}
//右上
for(int i=row-1,j=col+1;i>=0&&j<n;i--,j++)
{
if(board[i][j]=='Q') return false;
}
//左上
for(int i=row-1,j=col-1;i>=0&&j>=0;i--,j--)
{
if(board[i][j]=='Q') return false;
}
return true;
}

总结

1
2
3
4
5
6
for xuanze in liebiao:
//筛选不符项
if(!valid()) continue;
//做选择
//下一个递归
//撤销选择

是否有效,路径,结束条件

463岛屿的周长

先判断是否是岛屿,是就加4,再判断该格子的上下左右是否是岛屿,是,则有重叠部分,结果-1

暴力

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int jisuan(vector<vector<int>>& grid) 
{
int res=0;
int r=grid.size();
int c=grid[0].size();
for(int i=0;i<r;i++)
{
for(int j=0;j<c;j++)
{
if(grid[i][j]==1)
{
res+=4;
if(i+1<r&&grid[i+1][j]==1) res-=1;
if(j+1<c&&grid[i][j+1]==1) res-=1;
if(i-1>=0&&grid[i-1][j]==1) res-=1;
if(j-1>=0&&grid[i][j-1]==1) res-=1;
}

}
}
return res;

}

或者4*陆地数-2*重叠的边数(即,往下走和往右走,遇到陆地了,edge+1)

dfs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int dfs(vector<vector<int>>& grid,int r,int c) 
{

//从一个岛屿走向边界,周长+1
if(r<0||r>=grid.size()||c<0||c>=grid[0].size()) return 1;

if(grid[r][c]==0) return 1;//从陆地走向水域,周长+1

if(grid[r][c]!=1) return 0;//说明遍历过了

grid[r][c]=2;//表示访问过

return dfs(grid,r-1,c)+ dfs(grid, r + 1, c)+ dfs(grid, r, c - 1)+ dfs(grid, r, c + 1);
}
int islandPerimeter(vector<vector<int>>& grid) {

int res=0;
for(int i=0; i<grid.size(); i++){
for(int j=0; j<grid[0].size(); j++){
if(grid[i][j]==1){
res+= dfs(grid, i, j);
}
}
}
return res;
}

水域大小

给一个矩阵,数值为0的点为水域,水域通过垂直、水平或者对角连接,就称为池塘,求池塘的大小(从小到大)

思路:dfs,求8个方向(行从-1,0,1到列-1,0,1),如果是水域,且没访问过,size++;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
int dfs(vector<vector<int> >a,vector<vector<bool> >& visit,int i,int j )
{
if(i<0||i>=a.size()||j<0||j>=a[0].size()||a[i][j]!=0||visit[i][j]) return 0;

int size=1;
visit[i][j]=true;
for(int r=-1;r<=1;r++)
{
for(int c=-1;c<=1;c++)
size+=dfs(a,visit,i+r,j+c);
}
return size;
}
int main(){

vector<vector<int> >a={
{0,2,1,0},
{0,1,0,1},
{1,1,0,1},
{0,1,0,1}
};
int row=a.size(),col=a[0].size();

vector<vector<bool> >visit(row,vector<bool>(col,false));
vector<int> result;
for(int i=0;i<row;i++)
for(int j=0;j<col;j++)
{
if(a[i][j]==0&&!visit[i][j])
{
int size=dfs(a,visit,i,j);
result.push_back(size);
}
}
for(int tem:result)
cout<<tem<<" ";
}

二分查找

1
2
3
4
5
6
7
8
9
10
11
int left=0,right=...;
while(...)//注意点
{
int mid=left+(right-left)/2;//防止溢出
if(nums[mid]==target)
...
else if(<)
left=mid+1;
else if(>)
right=mid-1;
}

寻找一个数

1
2
3
4
5
6
7
8
9
10
11
int l=0,r=nums.size()-1;
while(l<=r)
{
int mid=left+(right-left)/2;//防止溢出
if(nums[mid]==target)
return mid;
else if(<)
left=mid+1;
else if(>)
right=mid-1;
}

如果是有序数组 2 3 3 3 4 5 6,target为3,求得的索引会是2,得不到左边的3和右边的3

链表环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
struct List{
int val;
List* next;

List(int x):val(x),next(nullptr){};
};
List* huan(List* list)
{
List* p1=list;
List* p2=list;

while(p2)
{
p1=p1->next;
if(p2->next)
{
p2=p2->next->next;
}
else return nullptr;

if(p1==p2)
{
List *p3=list;
while(p3!=p1)
{
p1=p1->next;
p3=p3->next;
}
return p3;
}

}
return nullptr;
}
set<List*> st;
List* huan2(List* list)
{
List*p =list;
while(p)
{
if(st.count(p)) return p;
else
{
st.insert(p);
}
p=p->next;
}
return nullptr;
}
int main(){


// List* list=new List(3);
// list->next=new List(2);
// list->next->next=new List(0);
// list->next->next->next=new List(-4);
// list->next->next->next->next=list->next;
List* list=nullptr;
List* cur=nullptr;
int t;
while(cin>>t)
{
if(t==-1) break;
if(!list)
{
list=new List(t);//第一个节点
cur=list;
}
else
{
cur->next=new List(t);
cur=cur->next;
}
}
if(list&&cur)
cur->next=list->next;//环
// cur=list;
// // while(cur)
// // {
// // cout<<cur->val<<" ";
// // cur=cur->next;
// // }
cur=list;
List* p=huan2(cur);
cout<<p->val;
return 0;
}

链表相交

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
List* create(vector<int> a)
{
List* list=nullptr;
List* cur=nullptr;

for(int tem:a)
{
if(!list)
{
list=new List(tem);
cur=list;
}
else
{
cur->next=new List(tem);
cur=cur->next;
}

}
return list;
}
int main(){


// List* list=new List(3);
// list->next=new List(2);
// list->next->next=new List(0);
// list->next->next->next=new List(-4);
// list->next->next->next->next=list->next;

vector<int> a={4,1,8,4,5};
vector<int> b={5,0,1,8,4,5};
List* p1=create(a);
List* p2=new List(5);
List*cur =p1;

int i=0;
while(i<2)
{
cout<<cur->val<<endl;
cur=cur->next;
i++;
}
// cout<<cur->val<<endl;
p2->next=new List(0);
p2->next->next=new List(1);
p2->next->next->next=cur;
List* p3=jiao(p1,p2);
cout<<p3->val;
return 0;
}

BFS

找最短路径

本质是图,从起点走到终点,求最短路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int BFS(Node start, Node target)
{
queue<Node> q;
set<Node> visited;//避免回头

q.push(start);
visited.insert(start);

while (!q.empty())
{
int size = q.size();
for (int i = 0; i < size; i++)
{
Node cur = q.front();
q.pop();
if (cur == target)
return step;

for (auto x : cur的相邻结点)
{
if (visited.count(x) == 0)
{
q.push(x);
visited.insert(x);
}
}
}
}
//否则没有找到目标结点
}

动态规划

核心问题:穷举

解决对象:一般求最值

dp前提:

1.具有最优子结构

2.通过子问题最值得到原问题最值

3.正确的状态转移方程

优化:备忘录、DP table

状态转移方程

明确base case ->明确 状态 -> 明确 选择->定义 dp数组的含义

基本框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 自顶向下递归的动态规划
def dp(状态1, 状态2, ...):
for 选择 in 所有可能的选择:
# 此时的状态已经因为做了选择而改变
result = 求最值(result, dp(状态1, 状态2, ...))
return result

# 自底向上迭代的动态规划
# 初始化 base case
dp[0][0][...] = base case
# 进行状态转移
for 状态1 in 状态1的所有取值:
for 状态2 in 状态2的所有取值:
for ...
dp[状态1][状态2][...] = 求最值(选择1,选择2...)

以斐波那契为例(重叠子问题的消除方法)

它不是求最值,严格来讲不是动态规划问题

暴力递归

1
2
3
4
int fib(int N) {
if (N == 1 || N == 2) return 1;
return fib(N - 1) + fib(N - 2);
}

如果n=20,那么自顶向下的递归树就是

image-20230806174428184

会遇到重叠子问题,比如f(17),上述递归树就是说,想知道f(20),去求f(19)和f(18)。。。直至求f(1)和f(2)

递归时间的复杂度就是*子问题个数(结点总数)解决一个子问题所需时间(比如循环算法)

上述时间为O(2ⁿ)*O(1)

为了优化,引入了备忘录解法递推解法

优化

备忘录

自顶向下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int fib(int n)
{
//初始化为0
vector<int> memo(n+1,0);
return dp(memo,n);
}

int dp(vector<int>memo,int n)
{
//base case
if(n==0||n==1) return 0;
//如果计算过
if(memo[n]!=0) return memo[n];
memo[n]=dp(memo,n-1)+dp(memo,n-2);
return memo[n];
}

相当于将递归树大量冗余的部分进行了 剪枝

减少了子问题中结点的个数

子问题就是 f(1), f(2), f(3)f(20),n个值

时间复杂度O(n)

递推

自底向上

1
2
3
4
5
6
7
8
9
10
11
12
13
int fib(int n)
{
if(n==0) return 0;
int *dp=new int[n+1];
//base case
dp[0]=0;dp[1]=1;
//状态转移
for(int i=2;i<=n;i++)
{
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}

滚动

上述的斐波那契算法,会发现,当前n状态只和n-1状态、n-2状态有关

所以可以

1
2
3
4
5
6
7
8
9
10
11
12
int fit(int n)
{
if(n==0||n==1) return n;
int tem1=0,tem2=0;
for(int 2=0;i<=n;i++)
{
int result=tem1+tem2;
tem2=tem1;
tem1=result;
}
return tem1;
}

凑零钱问题(最优子结构)

最优子结构满足条件:子问题间独立

给你 k 种面值的硬币,面值分别为 c1, c2 ... ck,每种硬币的数量无限,再给一个总金额 amount,问你最少需要几枚硬币凑出这个金额,如果不可能凑出,算法返回 -1

暴力递归法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int dp(vector<int> coin,int amount)
{
if(amount==0) return 0;//base
else if(amount<0) return -1;
int res=INT_MAX;
for(auto a:coin)
{
//计算子问题
int subcoin=dp(coin,amount-coin);
if(subcoin==-1) continue;
//子问题中选择最优解
res=min(res,subcoin+1);//+1表示使用了当前硬币
}
return res==INT_MAX ? -1:res;
}

为了优化,这里用递推

定义dp[i],目标金额为i,至少需要dp[i]枚硬币

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int findcoin(vector<int> a,int amount)
{
if(amount==0) return 0;
vector<int> dp(amount+1,amount+1);
dp[0]=0;
//遍历dp数组
for(int i=0;i<dp.size();i++)
{
//遍历硬币
for(int tem:a)
{
//别忘了,i现在指代的是目标金额,tem是coin的元素
if(i-tem<0) continue;
dp[i]=min(dp[i],dp[i-tem]+1);
}

}
return (dp[amount]==amount+1)?-1:dp[amount];
}

300.最长递增子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:

1
2
3
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2:

1
2
输入:nums = [0,1,0,3,2,3]
输出:4

示例 3:

1
2
输入:nums = [7,7,7,7,7,7,7]
输出:1

1.去定义一个dp数组,其意义为从nums取前i个元素时,最长递增长度为dp[i]

2.那么i相当于每次新数组的最后一位,那么再来个for循环,从0遍历到i

3.如果nums[j]<nums[i],那么dp[i]=max(dp[i],dp[j]+1);这个+1表示使用了当前数

4.更新res

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int findlength(vector<int> nums)
{
int res=0;
int n=nums.size();
if(nums.size()==0) return 0;
vector<int>dp(n+1,0);//记dp[i]表示一个递增数组,元素个数为i时的最长递增长度为dp[i]
for(int i=0;i<nums.size();i++)//定义i为数组的最后一位
{
dp[i]=1;
for(int j=0;j<i;j++)//从0遍历到i
{
if(nums[i]>nums[j])//
dp[i]=max(dp[i],dp[j]+1);
}
res=max(res,dp[i]);
}
return res;
}

时间复杂度O(n²)

滑动窗口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def findSubArray(nums):
N = len(nums) # 数组/字符串长度
left, right = 0, 0 # 双指针,表示当前遍历的区间[left, right],闭区间
sums = 0 # 用于统计 子数组/子区间 是否有效,根据题目可能会改成求和/计数
res = 0 # 保存最大的满足题目要求的 子数组/子串 长度
while right < N: # 当右边的指针没有搜索到 数组/字符串 的结尾
sums += nums[right] # 增加当前右边指针的数字/字符的求和/计数
while 区间[left, right]不符合题意: # 此时需要一直移动左指针,直至找到一个符合题意的区间
sums -= nums[left] # 移动左指针前需要从counter中减少left位置字符的求和/计数
left += 1 # 真正的移动左指针,注意不能跟上面一行代码写反
# 到 while 结束时,我们找到了一个符合题意要求的 子数组/子串
res = max(res, right - left + 1) # 需要更新结果
right += 1 # 移动右指针,去探索新的区间
return res

作者:负雪明烛
链接:https://leetcode.cn/problems/max-consecutive-ones-iii/solutions/609055/fen-xiang-hua-dong-chuang-kou-mo-ban-mia-f76z/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1004 最大连续1的个数

最大可翻转k个0,求数组中连续1最大的个数

1
2
3
4
输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。

转换为滑动窗口问题,如果最大的窗口中的0的个数为k,就可以组成最大连续1的个数

双指针法,左指针控制左索引i,右指针控制右索引j 最大个数为j-i+1,记录0的个数zero

先遍历数组,如果nums[i]=0,zero++

一旦zero>k,就需要减少zero,那就是将左指针向右滑动,直到zero<=k,更新左索引

更新最大值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int longestOnes(vector<int>& nums, int k) {
int res=0,zero=0,left=0;
for(int r=0;r<nums.size();++r)
{
if(nums[r]==0) ++zero;
while(zero>k)
{
if(nums[left]==0)
{
--zero;
}
left++;
}
res=max(res,r-left+1);
}
return res;
}

牛客

表达式求值

wp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
int pos = 0;
bool priority(char m, char n) {//判断优先度
if (m == '(') return false;
else if ((m == '+' || m == '-') && (n == '*' || n == '/')) return false;

return true;
}
void cal(stack<char>& op, stack<int>& num) {

int b = num.top();//先弹出来的属于第二个数
num.pop();
int a = num.top();
num.pop();
char zifu = op.top(); //栈顶运算符
op.pop();
if (zifu == '+') a = a + b;
else if (zifu == '-') a = a - b;
else if (zifu == '*') a = a * b;
else a = a / b;
num.push(a);//计算后放回去
}
int main() {
string s;
cin >> s;
stack<char> op;
stack<int> num;

op.push('(');//先加个左括号
s += ')';
bool flag = false;//是否出现了数字
for (int i = 0; i < s.length(); i++) {
if (s[i] == '(') op.push('(');//遇到括号,放进op栈
else if (s[i] == ')') {//遇到右括号,计算,直到遇到左括号
while (op.top() != '(') {
cal(op, num);
}
op.pop();//将左括号弹出
} else if (flag) { //出现了数字

//判断op栈顶优先于当前字符,计算
while (priority(op.top(), s[i])) {
cal(op, num);
}
op.push(s[i]);//否则加入新字符
flag = false;
} else {
int j = i;
if (s[j] == '-' || s[j] == '+') i++;
while (isdigit(s[i])) i++;
string temp = s.substr(j, i - j);
num.push(stoi(temp));
i--;
flag = true;
}

}
cout << num.top() << endl;


return 0;

}

错误代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
void cal(vector<char> &op,stack<int> &num)
{

char zifu=op[0];
op.erase(op.begin());
if(zifu=='+')
{
int a=num.top();
num.pop();
int b=num.top();
num.pop();
num.push(a+b);
}
if(zifu=='-')
{
int a=num.top();
num.pop();
int b=num.top();
num.pop();

num.push(b-a);
}
if(zifu=='*')
{
int a=num.top();
num.pop();
int b=num.top();
num.pop();

num.push(b*a);
}
if(zifu=='/')
{
int a=num.top();
num.pop();
int b=num.top();
num.pop();
//不考虑b=0
num.push(b/a);
}


}
int main() {
string s;
cin>>s;

stack<char> op;
vector<char> opnew;//后缀操作符记录
stack<int> num;
string n="";
for(int i=0;i<s.size();i++)
{
//
if(s[i]=='+'||s[i]=='-'||s[i]=='*'||s[i]=='/'||s[i]=='(')
{
op.push(s[i]);
num.push(stoi(n));
n="";
}
else if(s[i]==')')
{
while(op.top()!='(')
{
opnew.push_back(op.top());
op.pop();

if(op.top()=='(')
{
op.pop();
break;
}
}

}
else
{
n+=s[i];
}
}
num.push(stoi(n));//获取最后一个值

//将op剩余的操作符给opnew
while(!op.empty())
{
opnew.push_back(op.top());
op.pop();
}
// for(auto a:opnew)
// {
// cout<<a<<endl;
// }
// while(!num.empty())
// {
// cout<<num.top()<<endl;
// num.pop();
// }
while(!opnew.empty())
{
cal(opnew,num);
}
//int result=0;

cout<<num.top();
// cout<<num.top()<<" "<<num.size()<<endl;
// num.pop();
// cout<<num.top();


}

/
*
10
5
400

尼科彻斯定理

a^3(a为任意)=连续a个奇数的和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
   int n;
cin>>n;

int num=n*n*n;
int flag=-1,start=1;
for(int j=1;j<num;j+=2)
{
int co=0,result=0;
while(co<n)
{
result+=j+co*2;
co++;
}
if(result==num)
{
start=j;
flag=1;
break;
}

}
if(flag==1)
{
int co=0;
string s="";
while(co<n)
{
s+=to_string(start+co*2)+"+";
co++;
}
cout<<s.substr(0,s.size()-1);
}
return 0;
}

进阶那就是等差数列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main() {
int n;
cin>>n;

int num=n*n*n;
//sn=na+n(n-1)*d/2 找首项的问题
int a=(num-n*(n-1))/n;
for(int i=0;i<n;i++)
{
cout<<to_string(a+i*2);
if(i!=n-1) cout<<"+";
}
return 0;
}

公共子串计算

查找、动态规划

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
int main() {
string s1,s2;
cin>>s1>>s2;
int n=s1.size(),m=s2.size();
//int len1=s1.size(),len2=s2.size();
int maxlen=0;
vector<vector<int> >a(n+1,vector<int>(m+1,0));
//a[i][j] 即s1的前i个字符与s2的前j个字符的最大公共子串长度
//基础值填充
// for(int i=0;i<n;i++)
// {
// if(s1[0]==s2[i])
// {
// a[0][i]=1;
// maxlen=1;
// }
// //else a[0][i]=0;
// }
// for(int i=0;i<m;i++)
// {
// if(s1[i]==s2[0])
// {
// a[i][0]=1;
// maxlen=1;
// }
// //else a[i][0]=0;
// }
for(int i=1;i<=n;i++)//注意是
for(int j=1;j<=m;j++)
{
if(s1[i-1]==s2[j-1])//i为尾结点与j尾结点为止的字符相等
{
a[i][j]=a[i-1][j-1]+1;//
maxlen=max(maxlen,a[i][j]);
}
}
cout<<maxlen;
}

字符串字符匹配

一个短字符串,一个长字符串,短串的字符在长串有所出现即可,不用管次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
int main() {
string t1,t2;
while(cin>>t1>>t2)//多组样例输入
{
int n=t1.size(),m=t2.size();
map<char,int>mp;
for(int i=0;i<n;i++)
{
mp[t1[i]]+=1;
}
// for(auto a:mp)
// cout<<a.first<<","<<a.second<<endl;
for(auto& list:mp)//这里要有&,否则不会改变
for(int j=0;j<m;j++)
{
if(list.second!=0)
{
if(list.first==t2[j])
list.second=0;//list.second--不可取,只要子串的字符出现过
//即可,不用管次数
}
}
int flag=-1;
//cout<<endl;
for(auto a:mp)
{
// cout<<a.first<<","<<a.second<<endl;
if(a.second>0)
{
cout<<"false"<<endl;
flag=1;
break;
}
}
if(flag<0) cout<<"true"<<endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
   string t1,t2;
while(cin>>t1>>t2)//多组样例输入
{
int n=t1.size(),m=t2.size();
set<char> set1;
for(int i=0;i<m;i++)
{
set1.insert(t2[i]);
}
// for(auto a:mp)
// cout<<a.first<<","<<a.second<<endl;
int flag=-1;
for(auto list:t1)
{
if(!set1.count(list))
{
cout<<"false";
flag=1;
break;
}
}
if(flag<0) cout<<"true";

}
}

华为机试

1.字符串分割

给定一个非空字符串S,其被N个‘-’分隔成N+1的子串,给定正整数K,要求除第一个子串外,其余的子串每K个字符组成新的子串,并用‘-’分隔。对于新组成的每一个子串,如果它含有的小写字母比大写字母多,则将这个子串的所有大写字母转换为小写字母;反之,如果它含有的大写字母比小写字母多,则将这个子串的所有小写字母转换为大写字母;大小写字母的数量相等时,不做转换。
输入描述:
输入为两行,第一行为参数K,第二行为字符串S。
输出描述:
输出转换后的字符串。
示例1
输入
3
12abc-abCABc-4aB@
输出
12abc-abc-ABC-4aB-@
说明
子串为12abc、abCABc、4aB@,第一个子串保留,后面的子串每3个字符一组为abC、ABc、4aB、@,abC中小写字母较多,转换为abc,ABc中大写字母较多,转换为ABC,4aB中大小写字母都为1个,不做转换,@中没有字母,连起来即12abc-abc-ABC-4aB-@
示例2
输入
12
12abc-abCABc-4aB@
输出
12abc-abCABc4aB@
说明
子串为12abc、abCABc、4aB@,第一个子串保留,后面的子串每12个字符一组为abCABc4aB@,这个子串中大小写字母都为4个,不做转换,连起来即12abc-abCABc4aB@

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include<iostream>
#include<cstring>
using namespace std;
string zichuan(string s) //将子串转换大小写
{
int xiaoxie=1,daxie=1; //统计小写与大写数量
for(int i=0;i<s.size();i++)
{
if(s[i]>='a'&&s[i]<='z')
xiaoxie++;
if(s[i]>='A'&&s[i]<='Z')
daxie++;
}

if(xiaoxie>daxie) //若小写的多,将大写字母转化为小写。
for(int j=0;j<s.size();j++)
if(s[j]>='A'&&s[j]<='Z')
s[j]=(s[j]-'A')%26+'a';

if(xiaoxie<daxie)
for(int j=0;j<s.size();j++)
if(s[j]>='a'&&s[j]<='z')
s[j]=(s[j]-'a')%26+'A';

return s;
}

int main()
{
int n;
cin>>n;
string s;
cin>>s;
string ch1="",ch=""; //ch1记为第一子串;ch为其它子串总和
int co=0,co1=0,flag=0; //co为ch的长度,co1用来统计从0开始的子串个数,flag用来区分是否是第一字串
for(int i=0;i<s.size();i++)
{
char c=s[i];
if(c=='-')
flag=1;
if(flag==0&&c!='-')
{
ch1+=c; //第一个字符串

}
if(flag==1&&c!='-')
{
ch+=c;
co++; //
}
}
//已知ch1,co,ch了
int t=co-co%n;//从该位置往后,字符数是不足k个的
// cout<<t<<endl<<ch;
string ch2=""; //子字符串
for(int j=0;j<co;j++)
{
ch2+=ch[j];
co1++;
if(co1<=t) //<t不行
{
if(co1%n==0)
{
ch1=ch1+'-'+zichuan(ch2);
//cout<<ch2<<co1<<endl;
ch2="";//清零
}
}
else if(co1==co)
{
ch1=ch1+'-'+zichuan(ch2);
}
}
cout<<ch1<<endl<<co<<endl<<t;
//cout<<zichuan(ch)<<endl;

}

2.组成最大数

小组中每位都有一张卡片
卡片是6位以内的正整数
将卡片连起来可以组成多种数字
计算组成的最大数字

输入描述

,分割的多个正整数字符串
不需要考虑非数字异常情况
小组种最多25个人

输出描述

最大数字字符串

示例一

输入

1
22,221

输出

1
22221

思想

长度相同,数值大的在前;

长度不同,拼接值大的在前

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include<iostream>
#include<vector>
#include<sstream>
#include<algorithm>
using namespace std;
struct cmp{
bool operator()(string a,string b)
{
string c1=a+b;
string c2=b+a;
return c1>c2;
}
}sds;
int main()
{
vector<string> s;
string t;
cin>>t;
istringstream iss(t);
string token;
while(getline(iss,token,','))
{
s.push_back(token);

}
iss.clear();

// while(cin>>num)
// {
// s.push_back(num); //先获取值
// if(cin.get()=='\n') break;

// }
sort(s.begin(),s.end(),cmp());//或者asd

// for(vector<string>::iterator it=s.begin();it!=s.end();it++)
// {
// cout<<*it<<endl;
// }

string result="";
for(int i=0;i<s.size();i++) //存在[0,0]的情况。这样处理会是00
result+=s[i];

if(result[0]=='0')
{
cout<<'0';
return 0;
}
cout<<result;
return 0;
}

*拼接最大值

给定长度分别为 m 和 n 的两个数组,其元素由 0-9 构成,表示两个自然数各位上的数字。现在从这两个数组中选出 k (k <= m + n) 个数字拼接成一个新的数,要求从同一个数组中取出的数字保持其在原数组中的相对顺序。

求满足该条件的最大数。结果返回一个表示该最大数的长度为 k 的数组。

说明: 请尽可能地优化你算法的时间和空间复杂度。

示例 1:

输入:
nums1 = [3, 4, 6, 5]
nums2 = [9, 1, 2, 5, 8, 3]
k = 5
输出:
[9, 8, 6, 5, 3]
示例 2:

输入:
nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
输出:
[6, 7, 6, 0, 4]
示例 3:

输入:
nums1 = [3, 9]
nums2 = [8, 9]
k = 3
输出:
[9, 8, 9]

思想

求子问题,nums1中取i个组成最大的子序列,nums2中取k-i个,组成最大的子序列,合并,求所有的i的可能

要使得子序列最大,那从左到右是递减的

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
class Solution {

public:
vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {

vector<int> max_n1,max_n2;
vector<int> maxseq(k,0);
int n1=nums1.size(),n2=nums2.size();
int start=max(0,k-n2);//表示从nums1中取得的下限
int end=min(k,n1);//表示从nums1中取得的上限
/*
如果k-n2为负数,意味着不需要在
*/
for(int i=start;i<=end;i++) //
{
vector<int> max_n1=pick_max(nums1,i);
vector<int> max_n2=pick_max(nums2,k-i);
vector<int> curmerge=merge(max_n1,max_n2);
if(compare(curmerge,0,maxseq,0)>0)
{
maxseq=curmerge;
}
}
return maxseq;
}

vector<int> pick_max(vector<int> num,int k)//个数为k的最大序列
{

vector<int> st;
int drop=num.size()-k;//允许丢弃数
for(int i=0;i<num.size();i++)
{
while(drop>0&&!st.empty()&&st.back()<num[i])//1.drop和drop>0做条件不一样
{
st.pop_back();
drop--;
}
//st.push_back(num[i]);
if(st.size()<k) //没有取够k个
st.push_back(num[i]);
else //取够了就继续丢弃
{
drop--;
}
}
//st.resize(k);
return st;
}
vector<int> merge(vector<int> a,vector<int> b)
{
vector<int> result;
// if(a.size()==0) return b;
// if(b.size()==0) return a;
int index1=0,index2=0;
while(index1<a.size()&&index2<b.size())
{
//vector默认字典序排序
//如123与179,7>2
// if(vector<int>(a.begin()+index1,a.end()) > vector<int>(b.begin()+index2,b.end()) )
if(compare(a,index1,b,index2)>0)
{
result.push_back(a[index1++]);
}
else
{
result.push_back(b[index2++]);
}

}
//将剩余值放进result(a,b长度不同导致while提前结束,有剩余值未处理)
copy(a.begin()+index1,a.end(),back_inserter(result));
copy(b.begin()+index2,b.end(),back_inserter(result));

// 可以替换如下
// while (index1 < a.size()) {
// result.push_back(a[index1++]);
// }
// while (index2 < b.size()) {
// result.push_back(b[index2++]);
// }
return result;
// num.push_back(a);
}

//比较字典序
int compare(vector<int> a,int i,vector<int> b,int j)//2.错在这里,bool和int是不一样的,淦!
{
while(i<a.size()&&j<b.size())
{
//如123与179,7>2
int diff=a[i]-b[j];
if(diff!=0) return diff;
i++;
j++;
}
//剩下的值
return (a.size()-i)-(b.size()-j);
}
};

1.范围选择问题

1
2
3
4
vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
...
for(int i=0;i<=k;i++) //一旦输入如下,是不对的
}

输入:

[6,7] [6,0,4] 5

输出:

[7,6,0,4]

预期结果:

[6,7,6,0,4]

过程如下:

i=0,k-i=5
xulie1

xulie2
6 0 4
curmerge
6 0 4
maxseq=
6 0 4
i=1,k-i=4
xulie1
7
xulie2
6 0 4
curmerge
7 6 0 4
maxseq=
7 6 0 4
i=2,k-i=3
xulie1
6 7
xulie2
6 0 4
curmerge
6 7 6 0 4
maxseq=
7 6 0 4
i=3,k-i=2
xulie1
6 7
xulie2
6 4
curmerge
6 7 6 4
maxseq=
7 6 0 4
i=4,k-i=1
xulie1
6 7
xulie2
6
curmerge
6 7 6
maxseq=
7 6 0 4
i=5,k-i=0
xulie1
6 7
xulie2

curmerge
6 7
maxseq=
7 6 0 4
7 6 0 4

分析可得,目的是要求5位,但是从一开始的结果就有3位,4位,甚至是2位,究其原因是没有判断两数组各自的长度是否够k位。

一开始就限定nums1要取的元素

如果k-nums2.size()>0, 就说明nums2的数组元素不够k个,那就至少从nums1中取k-nums2.size()个;不然完全可以从0开始。

如果k与nums1长度相比,nums1本身不够k个,那nums1最多取nums1.size()个。

解决方法

1
2
3
4
5
6
vector<int> max_n1,max_n2;
vector<int> maxseq(k,0);
int m = nums1.size(), n = nums2.size();
int start = max(0, k - n), end = min(k, m);
for(int i= start;i<=end;i++) //
{

start=2 ,end=2
6 7
xulie2
6 0 4
curmerge
6 7 6 0 4
6 7 6 0 4

2.pick_max中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  for(int i=0;i<num.size();i++)
{
while(drop>0&&!st.empty()&&st.back()<num[i])//1.drop和drop>0做条件不一样
{
st.pop_back();
drop--;
}
//st.push_back(num[i]);
if(st.size()<k) //没有取够k个
st.push_back(num[i]);
else //取够了就继续丢弃
{
drop--;
}
}
//st.resize(k);
return st;
}

可以进行如下替换
for(int i=0;i<num.size();i++)
{
while(drop>0&&!st.empty()&&st.back()<num[i])//1.drop和drop>0做条件不一样
{
st.pop_back();
drop--;
}
st.push_back(num[i]);
}
st.resize(k);
return st;
}

3.vector也是默认字典序排列的

在compare中可以

1
2
3
4
5
6
7
8
9
if(vector<int>(a.begin()+index1,a.end()) > vector<int>(b.begin()+index2,b.end()) )
if(compare(a,index1,b,index2)>0)
{
result.push_back(a[index1++]);
}
else
{
result.push_back(b[index2++]);
}

3.统计射击成绩

题目描述

给定一个射击比赛成绩单
包含多个选手若干次射击的成绩分数
请对每个选手按其最高三个分数之和进行降序排名
输出降序排名后的选手ID序列
条件如下:

  1. 一个选手可以有多个射击成绩的分数 且次序不固定
  2. 如果一个选手成绩小于三个 则认为选手的所有成绩无效 排名忽略该选手
  3. 如果选手的成绩之和相等,则成绩相等的选手按照其ID降序排列

输入描述

输入第一行:一个整数 N
表示该场比赛总共进行了N次射击
产生N个成绩分数 2 <= N <= 100
输入第二行 一个长度为N的整数序列
表示参与本次射击的选手Id
0 <= ID <= 99
输入第三行是长度为N的整数序列
表示参与每次射击的选手对应的成绩
0 <= 成绩 <= 100

输出描述

符合题设条件的降序排名后的选手ID序列

示例一

输入

1
2
3
13
3,3,7,4,4,4,4,7,7,3,5,5,5
53,80,68,24,39,76,66,16,100,55,53,80,55

Copy

输出

1
5,3,7,4

Copy

说明

该场射击比赛进行了13次,参赛选手为3 4 5 7
3号选手的成绩为53 80 55最高三个成绩的和为 188
4号选手的成绩为24 39 76 66最高三个和为181
5号选手的成绩为53 80 55 最高三个和为188
7号选手成绩为68 16 100 最高三个和184
比较各个选手最高三个成绩的和
3 = 5 > 7 > 4
由于35成绩相等 且5 > 3 所以输出为5,3,7,4

分析

第二行,选手id,顺序不固定

第三行,对应选手id所取得的成绩score。

1.想到map键值对。

由于一个选手对应多个成绩

且成绩若多于3个,需要取最高的三个

2.若少于3个,该成绩无效(输出时,需要判断)

综上的思路是

map<int,vector<int> > scores;

理由:键为id,值为多个成绩

输入时先获取整数序列值,需要将逗号分割,再将字符串转数字

主函数下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
int n;
cin>>n;
string input1,input2;
cin>>input1>>input2;
vector<int> id; //用于接收第二行
vector<int> score; //接收第三行

string ch="";//子串
//接收输入
for(int i=0;i<intput1.size();i++)
{
if(input1[i]!=',')
ch+=input1[i];
if(input1[i]==','||i==input1.size()-1)
{
id.push_back(atoi(ch.c_str())); //将字符串转化为数字并存入id中
}
}
ch="";
for(int i=0;i<intput2.size();i++)
{
if(input1[2]!=',')
ch+=input2[i];
if(input2[i]==','||i==input2.size()-1)
{
score.push_back(atoi(ch.c_str())); //将字符串转化为数字并存入id中
}
}
//获取完值,想办法键值对应
map<int,vector<int> > scores;

for(int i=0;i<n;i++)
{
int key=id[i];
int value=score[i];
map<int,vector<int> >::iterator it=scores.find(key);
if(it !=it.end()) //找到该键
{
scores[key].push_back(value);
}
else //如果没有这个键,添加(比如第一次时,key=4,但找不到,先添加值)
{
vector<int> list;
list.push_back(value);

scores[key]=list;
}
}
//此时是一个id对应多个成绩。
//对成绩筛选并加和。
for(map<int,vector<int> >::iterator it=scores.begin();it!=scores.end();it++)
{
int num=0;
if(it->second.size()>=3)//如果成绩数不少于3个
{
sort(scores.rbegin(),scores.rend());//降序排列
num=it->second[0]+it->second[1]+it->second[2];//最高的三个成绩
}
else
{
num=0; //成绩作废为0;
}
it->second.clear();//清空值
it->second.push_back(num);
}
//键值对排序,因为要按照值排序,且值相同时,id大的在前。
vector<pair<int,vector<int> > > sortedsc(scores.begin(),scores.end());//赋值给sortedsc,用来处理单个键值对情况。
sort(sortedsc.begin(),sortedsc.end(),Compare);//Compare为自定义算法

vector< pair<int,vector<int> > >::iterator it;
for(it=sortedscores.begin(); it!=sortedscores.end();it++)
{
if(it->second[0]>0)
{
cout<<it->first;
if(it+1!=sortedscores.end()) //如果不是最后一位,输出逗号
cout<<",";
}


}

自定义函数

1
2
3
4
5
6
7
8
9
struct Compare
{
bool operator()(pair<int,vector<int> >&a,pair<int,vector<int> >&b)/*当你定义一个函数对象并希望它能够像函数一样被调用时,你需要重载函数调用运算符 ()。在函数调用运算符的定义中,括号是必需的,用于指示该函数对象应该如何被调用。*/
{
if(a.second==b.second) //值相同,找键值大的
return a.first>b.first;
return a.second>b.second;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct CustomComparator {   
bool operator()(int a, int b) const {
// Custom comparison logic
return a < b;
// Sort integers in ascending order }
};

...

int main()
{
...
sort(v.begin(),v.end(),CustomComparator());

}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include<iostream>
#include<map>
#include<vector>
#include<algorithm>
#include<cstdlib>
#include<string>
using namespace std;
struct Compare
{
bool operator()(pair<int,vector<int> >&a,pair<int,vector<int> >&b)/*当你定义一个函数对象并希望它能够像函数一样被调用时,你需要重载函数调用运算符 ()。在函数调用运算符的定义中,括号是必需的,用于指示该函数对象应该如何被调用。*/
{
if(a.second==b.second) //值相同,找键值大的
return a.first>b.first;
return a.second>b.second;
}
};
int main()
{
int n;
cin>>n;
string input1,input2;
cin>>input1>>input2;
vector<int> id; //用于接收第二行
vector<int> score; //接收第三行

string ch="";//子串
//接收输入
for(int i=0;i<input1.size();i++)
{
if(input1[i]!=',')
ch+=input1[i];
if(input1[i]==','||i==input1.size()-1)
{
id.push_back(atoi(ch.c_str())); //将字符串转化为数字并存入id中
// cout<<ch<<endl;
ch="";//清零

}
}
ch="";
for(int i=0;i<input2.size();i++)
{
if(input2[i]!=',')
ch+=input2[i];
if(input2[i]==','||i==input2.size()-1)
{
score.push_back(atoi(ch.c_str())); //将字符串转化为数字并存入id中
// cout<<ch<<endl;
ch="";//清零
}
}
//获取完值,想办法键值对应
map<int,vector<int> > scores;

for(int i=0;i<n;i++)
{
int key=id[i];
int value=score[i];
map<int,vector<int> >::iterator it=scores.find(key);
if(it !=scores.end()) //找到该键
{
scores[key].push_back(value);
}
else //如果没有这个键,添加(比如第一次时,key=4,但找不到,先添加值)
{
vector<int> list;
list.push_back(value);

scores[key]=list;
}
// cout<<key<<" "<<value<<endl;
}
//此时是一个id对应多个成绩。
//对成绩筛选并加和。
for(map<int,vector<int> >::iterator it=scores.begin();it!=scores.end();it++)
{
int num=0;
if(it->second.size()>=3)//如果成绩数不少于3个
{
sort(it->second.rbegin(),it->second.rend());//降序排列
num=it->second[0]+it->second[1]+it->second[2];//最高的三个成绩
}
else
{
num=0; //成绩作废为0;
}
it->second.clear();//清空值
it->second.push_back(num);
// cout<<it->first<<" "<<num<<endl;
}
//键值对排序,因为要按照值排序,且值相同时,id大的在前。
vector<pair<int,vector<int> > > sortedsc(scores.begin(),scores.end());//赋值给sortedsc,用来处理单个键值对情况。
sort(sortedsc.begin(),sortedsc.end(),Compare());//Compare为自定义算法

vector< pair<int,vector<int> > >::iterator it;
for(it=sortedsc.begin(); it!=sortedsc.end();it++)
{
if(it->second[0]>0)
{
cout<<it->first;
if(it+1!=sortedsc.end()) //如果不是最后一位,输出逗号
cout<<",";
}


}
}

4.子串匹配

链接:https://www.nowcoder.com/questionTerminal/5382ff24fbf34a858b15f93e2bd85307?f=discussion
来源:牛客网

给定两个字符串 s和 t ,判断 s是否为 t 的子序列。

你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度n ~= 500,000),而 s 是个短字符串(长度 <=100)。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,”ace”是”abcde”的一个子序列,而”aec”不是)。

进阶:时间复杂度O(n) O(n)\ O(n) ,空间复杂度O(n) O(n)\ O(n)

输入描述:

1
2
3
共两行,第一行为字符串s,  第二行为字符串t
字符串t的长度 1<=n<=500000
字符串s的长度 1<=m<=100

输出描述:

1
输出true或者是false,true表示是s是t的子序列,false表示s不是t的子序列

示例1

输入

1
2
abc
ahbgdc

输出

1
true

双指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;

int main() {
string s, t;
cin>>s>>t;
int index=0;
for(int i=0;i<s.size();)
{
if(index>=t.size())
{
cout<<"false";
return 0;
}
if(s[i]==t[index])
{
i++;
}
index++;
}
cout<<"true";
return 0;
}

5.数据分类

题目描述

对一个数据a进行分类,
分类方法是,此数据a(4个字节大小)的4个字节相加对一个给定值b取模,
如果得到的结果小于一个给定的值c则数据a为有效类型,其类型为取模的值。
如果得到的结果大于或者等于c则数据a为无效类型。

比如一个数据a = 0x01010101b = 3
按照分类方法计算:(0x01 + 0x01 + 0x01 + 0x01) % 3 = 1
所以如果c等于2,则此a就是有效类型,其类型为1
如果c等于1,则此a是无效类型

又比如一个数据a = 0x01010103b = 3
按分类方法计算:(0x01 + 0x01 + 0x01 + 0x03) % 3 = 0
所以如果c = 2则此a就是有效类型,其类型为0
如果c = 0则此a是无效类型

输入12个数据,
第一个数据为c,第二个数据为b
剩余10个数据为需要分类的数据

请找到有效类型中包含数据最多的类型,
并输出该类型含有多少个数据

输入描述

输入12个数据用空格分割,
第一个数据为c,第二个数据为b
剩余10个数据为需要分类的数据。

输出描述

请找到有效类型中包含数据最多的类型,
并输出该类型含有多少个数据。

示例一

输入

1
3 4 256 257 258 259 260 261 262 263 264 265

输出

1
3

个人代码

将十进制转化为十六进制,说是四字节,那就是8个值,我用的vector(8,0)记录

十六进制加和,那就是8个值中两两组合,a*16+b

再将模后的值存入新的vector,用来统计出现次数,从大到小排序,输出最大值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
#include<map>
using namespace std;


vector<int> zijie(int num)//十进制处理成十六进制
{
//四个字节,8个值
vector<int> s(8,0); //默认0填充
//十六进制 //四字节
int index=0;
while(num/16!=0)
{
int shang=num/16;
int yushu=num%shang;
num=shang;
//s.push_back(yushu);
s[index]=yushu;
index++;
}
//一定有一个1;
s[index]=1;
reverse(s.begin(),s.end()); //反转
// for(int i=0;i<ss.size();i++)
// {
// cout<<ss[i]<<",";
// }
return s;
}
int jiahe(vector<int> s,int b)//十六进制各部分加和,再模b
{
int sum=0;
for(int i=0;i<s.size();i=i+2)//
{
sum+=s[i]*16+s[i+1];
}
return sum%b;
}
int main()
{
vector<int> ss;
int num;
while(cin>>num)
{
ss.push_back(num);
if(cin.get()=='\n') break;
}
// for(int i=0;i<ss.size();i++)
// {
// cout<<ss[i]<<",";
// }
int c=ss[0],b=ss[1];
vector<int> leixing;
for(int i=2;i<ss.size();i++)
{
leixing.push_back(jiahe(zijie(ss[i]),b));
}

//ss.clear();//只会重置size
// for(int i = 0; i < ss.size(); ++i)//删除ss的元素,以便之后使用
// {
// delete ss[i];
// }
// ss.clear();
//想要清空元素,重复利用ss,不行,嘶
vector<int> zhi(100,0);//暂时记为模的最大值为99吧
for(int i=0;i<leixing.size();i++)
{
// cout<<leixing[i]<<",";
if(leixing[i]<c)
{
zhi[leixing[i]]++; //比如模后的值是4,s[4]++
}
}
sort(zhi.begin(),zhi.end());//默认从小到大排序
reverse(zhi.begin(),zhi.end());//
cout<<zhi[0];
//cout<<jiahe(zhi,3);


return 0;
}

参考wp(map映射)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
#include<map>
using namespace std;
int jiahe(int num);
void solution(vector<int> s)//
{
int c=s[0];
int b=s[1];
map<int,int> mp;//映射
for(int i=2;i<s.size();i++)
{
int r=jiahe(s[i])%b;
if(r<c) mp[r]=mp.count(r)?mp[r]+1:1;//键值相同+1,没有则置1
}
int max=0;
// map<int,int>::iterator it;
for(const auto& pair:mp)
{
if(pair.second>max) max=pair.second;
}
cout<<max;
}
int jiahe(int num)//十六进制各部分加和
{
int sum=0;
for(int i=0;i<4;i++)//
{
sum+=static_cast<char>(num>>i*8);
}
return sum;
}
int main()
{
vector<int> ss;
int num;
while(cin>>num)
{
ss.push_back(num);
if(cin.get()=='\n') break;
}
solution(ss);

return 0;
}

map.find()是迭代器指向元素 if maps.find(1)==maps.end(),无该元素

map.count(),结果只为0或1

static_cast 强制类型转化操作

>>i*8 取字节 (i=0-3):

四字节32位是以大端方式排列的,

即最高位(最左)->最低位(最右)

259为例

1
2
3
4
5
6
int num=259;
cout<<hex<<259<<endl;
cout<<(num>>0)<<" char:"<<int(static_cast<char>(num>>0))<<endl;
cout<<(num>>8)<<" char:"<<int(static_cast<char>(num>>8))<<endl;
cout<<(num>>2*8)<<" char:"<<int(static_cast<char>(num>>16))<<endl;
cout<<(num>>3*8)<<" char:"<<int(static_cast<char>(num>>24))<<endl;

结果为:(不纠结了,就这么记着吧)

103
103 char:3
1 char:1
0 char:0
0 char:0

6.5键键盘

题目描述

有一个特殊的五键键盘
上面有ACtrl-CCtrl-XCtrl-VCtrl-A
A键在屏幕上输出一个字母A
Ctrl-C将当前所选的字母复制到剪贴板
Ctrl-X将当前选择的字母复制到剪贴板并清空所选择的字母
Ctrl-V将当前剪贴板的字母输出到屏幕
Ctrl-A选择当前屏幕中所有字母
注意:

  1. 剪贴板初始为空
  2. 新的内容复制到剪贴板会覆盖原有内容
  3. 当屏幕中没有字母时,Ctrl-A无效
  4. 当没有选择字母时Ctrl-CCtrl-X无效
  5. 当有字母被选择时ACtrl-V这两个输出功能的键,
    会先清空所选的字母再进行输出

给定一系列键盘输入,
输出最终屏幕上字母的数量

输入描述

输入为一行
为简化解析用数字12345分别代替ACtrl-CCtrl-XCtrl-VCtrl-A的输入
数字用空格分割

输出描述

输出一个数字为屏幕上字母的总数量

示例一

输入

1
1 1 1

Copy

输出

1
3

示例二

输入

1
1 1 5 1 5 2 4 4

输出

1
2

个人代码(仅试过用例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
using namespace std;
void solution(vector<int> ss)
{
int token=0,co=0;//缓冲区的字母数 与 目前字母数
int onxuanze=-1,onzimu=-1,c=-1;//被选择、有输入字母、复制操作
for(int i=0;i<ss.size();i++)
{
//token+=ss[i];
if(ss[i]==1) //输入值
{
//说明有输入,如果有选择命令,则覆盖
//没有则追加输入
// onzimu=1; //有值
// if(onxuanze>0) //选中值
if(ss[i-1]==5) //选中一定是紧挨的操作,否则不成立
{

co=1; //直接覆盖
}
else co++;
}

if(ss[i]==2)//复制
{

if(ss[i-1]==5)//要复制一定有选中,是紧挨的选中
{
token=co;
c=1;
}
}

if(ss[i]==3)//剪切
{
if(ss[i-1]==5)
{
token=co;
co=0;
onzimu=-9;//清空字母
c=1;//复制
}

}

int v=0;
if(ss[i]==4) //输出
{

if(c>0) //原地v
{
co=token;//这种是选中后,复制,再v,所以覆盖
v++;
}

//那追加v怎么办呢?
if(v>0) co+=token;
//如果选中了,直接v,那就是覆盖了
/*
它可以是选择,复制,再v,也是一样的覆盖
1 1 5 1 5 2 4 4应该是输出2,而不是3
它这种情况就是,选中后不一定马上v,会有复制的情况
*/
// if(ss[i-1]==5) co=token;
}
// if(ss[i]==5) //选中
// {
// if(onzimu>0) //有字母
// {
// onxuanze=1;
// //token=co; //当前选中数
// }

// }
}
cout<<co;
}
int main()
{
vector<int> ss;
string t;
while(cin>>t)
{
ss.push_back(stoi(t));
if(cin.get()=='\n') break;
}
solution(ss);
}

wp

用字符串cur记录当前的输出字母”A”

cur的长度即为最终输出

copy为缓冲区里的字母

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
void solution(vector<int> ss)
{
string copy=""; //剪切板
// string select="";
string cur=""; //当前面板
int flag=0; //是否选中
for(int op:ss){
switch(op)
{
case 1:
if(flag!=1)
{
cur+="A";
flag=0;
}
else
{
cur=""; //
cur+="A";
flag=0;
}
break;
case 2:
if((cur.size()!=0)&&(flag!=0)) //没有选中
{
copy=cur; //复制
}
break;
case 3:
if((cur.size()!=0)&&(flag!=0)){
copy=cur;
cur=""; //清零
}
break;
case 4: //输出
if((copy.size()!=0)&&(flag!=0)){
cur=copy;
flag=0;
}
else if(copy.size()!=0)//选中了
{
cur+=copy;
flag=0;
}
break;
case 5:
flag=1;
break;
default:
break;
}
}
cout<<cur.size();
}

7.检查是否存在满足条件的数字组合

题目描述

给定一个正整数数组,检查数组中是否存在满足规则的数字组合

规则:A = B + 2C

输入描述

第一行输出数组的元素个数。

接下来一行输出所有数组元素,用空格隔开。

输出描述

如果存在满足要求的数,在同一行里依次输出规则里A/B/C的取值,用空格隔开。

如果不存在,输出0。

备注

数组长度在3-100之间。
数组成员为0-65535,数组成员可以重复,但每个成员只能在结果算式中使用一次。如:数组成员为[0, 0, 1, 5],0出现2次是允许的,但结果0 = 0 + 2 * 0是不允许的,因为算式中使用了3个0。
用例保证每组数字里最多只有一组符合要求的解。

输入

4
2 7 3 0

输出

7 3 2

说明

7 = 3 + 2 * 2

个人代码(3for)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//a=b+2c,同一个数字最多出现1次
//a-b=2c =>(a-b)/2=c
//用例保证了只有一组符号要求的解
void solution(vector<int> s)
{
for(int i=0;i<s.size();i++){
for(int j=0;j<s.size();j++){
for(int k=0;k<s.size();k++){
if(i==j||i==k||k==j) continue;
if((s[i]-s[j])==2*s[k])
{
cout<<s[i]<<" "<<s[j]<<" "<<s[k];
exit(0);
}
if((s[i]-s[k])==2*s[j])
{
cout<<s[i]<<" "<<s[k]<<" "<<s[j];
exit(0);
}
}

}
}
cout<<0;
}

int main()
{
int length,num;
cin>>length;
vector<int> s;

while(cin>>num)
{
s.push_back(num);
if(cin.get()=='\n') break;
}
solution(s);
return 0;
}

*8.数组拼接

题目描述:

现在有多组整数数组,需要将他们合并成一个新的数组。合并规则,从每个数组里按顺序取出固定长度的内容合并到新的数组中,取完的内容会删除掉,如果该行不足固定长度或者已经为空,则直接取出剩余部分的内容放到新的数组中,继续下一行。如样例1,获得长度3,先遍历第一行,获得2,5,6;再遍历第二行,获得1,7,4;再循环回到第一行,获得7,9,5;再遍历第二行,获得3,4;再回到第一行,获得7,按顺序拼接成最终结果。

输入描述

第一行是每次读取的固定长度,0<长度<10
第二行是整数数组的数目0<数目<1000,
3~n行是需要合并的数组,不同的数组用回车换行分隔,
数组内部用逗号分隔。最大不超过100个元素

输出描述

输出一个新的数组,用逗号分隔

示例一

输入

1
2
3
4
3
2
2,5,6,7,9,5,7
1,7,4,3,4

Copy

输出

1
2,5,6,1,7,4,7,9,5,3,4,7

Copy

示例二

输入

1
2
3
4
5
4
3
1,2,3,4,5,6
1,2,3
1,2,3,4

Copy

输出

1
1,2,3,4,1,2,3,1,2,3,4,5,6

个人代码(有错误处)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
void solution(vector<string> &s,int m,int n)
{
string result="",token="",shuzhi="";

int begin=0,len=m+m,flag=m,tiaoguo=0;//len是m+逗号数+1(哎,逗号这个问题)
//flag遍历完的数组数,tiaoguo为当前是否循环

// substr()
while(flag)
{
for(int i=0;i<n;i++)//每个数组
{

//begin是每次取子串的起始位置,怎么处理begin>size()的问题
if((begin+len)<s[i].size()) //索引保持在正常范围内,即下一次循环还能截取
{
token+=s[i].substr(begin,len);
//cout<<"can substr"<<token<<endl;
}
else if(begin+len>s[i].size()&&begin<s[i].size()) //
{//下一次不能截取,那就直接读完
token=s[i].substr(begin); //直接取完
token+=","; //加个逗号
flag--;
// cout<<"ou substr"<<token;
}
else continue; //下次循环
result+=token;
token="";//清零

//shuzhi=s[i];//后面用到
}
//如果长度不一样,怎么确保哪些弄完,哪些没弄完

// if((begin+len)<shuzhi.size()) //防止下次substr溢出
begin+=len;//下一次截取起始位置
// else continue;
// if(tiaoguo>0) continue; //跳出这次循环
// if(begin+len<s[i].size())//substr的起始位置不超过字符本身,就是说还可以截取
// {
// token=s[i].substr(begin,len); //截取
// // tiaoguo=-1; //下一次循环不跳过
// }
// else //否则直接赋值
// {
// token=s[i];
// // tiaoguo=1;//跳出这次循环
// flag++;//跳出次数,即每次读m,读完的次数
// }
// result+=token;

}
cout<<result;
}

int main()
{
// int n,m;
// cin>>n>>m;
//getchar();//取走回车,为了下面的getline

string s;
int m,n;
cin>>m>>n;
vector<string> sw;
getchar(); //否则,n=2时,下文的getline只读一行,因为之前的回车读到了
// cin.ignore();
for(int i=0;i<n;i++)
{
getline(cin,s);//默认分隔符为'\n' cin会默认去除\n
sw.emplace_back(s);
}
solution(sw,m,n);

// cout<<sw.size();
// vector


}

面临的问题有:

1.因为是带着逗号来的,而且是字符串存储,双位数+逗号和单位数+逗号,难以把控,(举例1,123,4和1,2,3,4, 这个位置问题啊,阿西吧)

解决办法:想办法以整数添加吧;或者for循环里以“,”分割,添加m位添加数字。

2.substr的溢出问题,我用begin代表初始位置,捕捉m长度的数值,用子串字符串作为缓冲添加

解决办法:因为每次循环完,begin会增加m长度,更新substr的初始位置,会大过原有索引。

。。。必须去掉逗号的影响

9.数列描述

题目描述

有一个数列a[N] (N=60),从a[0]开始,每一项都是一个数字。数列中a[n+1]都是a[n]的描述。其中a[0]=1。

规则如下:

a[0]:1

a[1]:11(含义:其前一项a[0]=1是1个1,即“11”。表示a[0]从左到右,连续出现了1次“1”)

a[2]:21(含义:其前一项a[1]=11,从左到右:是由两个1组成,即“21”。表示a[1]从左到右,连续出现了两次“1”)

a[3]:1211(含义:其前一项a[2]=21,从左到右:是由一个2和一个1组成,即“1211”。表示a[2]从左到右,连续出现了1次“2”,然后又连续出现了1次“1”)

a[4]:111221(含义:其前一项a[3]=1211,从左到右:是由一个1、一个2、两个1组成,即“111221”。表示a[3]从左到右,连续出现了1次“1”,连续出现了1次“2”,连续出现了两次“1”)

请输出这个数列的第n项结果(a[n],0≤n≤59)。

输入描述

数列的第n项(0≤n≤59)

4

输出描述

数列的内容

111221

个人代码(有误)

怎么达成递归条件啊

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
vector<string> s(61);
string back(int num)
{
//a[n]就是对a[n-1]的描述
//即a[n-1]中,a[n-1][i]的情况,从左往右,出现次数与该值。。。
//例如 a[3]="1211",那么a[4]="111221"一次1,一次2,二次1
//那就是0-9,记为j,来个index记录一下,如果相等,对应位置a[j]++
//但面临的问题是,不知道是连续的呢,还是独立的呢
//

vector<int> index(10,0);

if(num==3)
{
// s[num]="1";
return "21";
}
// if(s[num])
int pre=s[num-1][0]-'0';
index[pre]++;
for(int i=1;i<back(num-1).size();i++)
{
int tem=s[num-1][i]-'0';//其中的值
if(tem==pre) //从左到右相同,连续统计
{
index[tem]++;
}
// string s=to_string(123);
// to_string(23);
else//相邻不同
{
s[num]+=to_string(index[pre])+to_string(pre);//给s[num];
index[tem]=1;//防止以前统计过,置为1
pre=tem;
}
}
s[num] += to_string(index[pre]) + to_string(pre);


return s[num];
}
int main()
{
back(4);
return 0;
}

个人代码(纠正后)

放弃递归想法,直接从已知一步步算到所求。比如已知s[0],s[1],求s[5];先算s[2],再算s[3]…直到s[5];

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
//vector<string> s(61,"");
vector<string> s(61);
void bianli(int num)
{
//a[n]就是对a[n-1]的描述
//即a[n-1]中,a[n-1][i]的情况,从左往右,出现次数与该值。。。
//例如 a[3]="1211",那么a[4]="111221"一次1,一次2,二次1
//那就是0-9,记为j,来个index记录一下,如果相等,对应位置a[j]++
//但面临的问题是,不知道是连续的呢,还是独立的呢
//

// if(s[num])

for(int i=2;i<=num;i++) //从第2项算到第num项
{
vector<int> index(10,0); //每次初始化为0
int pre=s[i-1][0]-'0'; //记录s[1]的第一个元素
index[pre]++; //对应下标相加
// cout<<"sdsd"<<": "<<pre<<endl;
for(int k=1;k<s[i-1].size();k++)
{
int tem=s[i-1][k]-'0';//取第k个值
//cout<<"s[i-1]"<<" "<<s[i-1].size()<<endl;
if(tem==pre) //如果从左到右相同,连续统计
{
index[tem]++;
}
// string s=to_string(123);
// to_string(23);
else//相邻不同
{
s[i]+=to_string(index[pre])+to_string(pre);//给s[i]赋值 统计数+对应数;
//s[i]="123";
// cout<<"出现次数: "<<index[pre]<<",对应值 "<<pre<<endl;
index[tem]=1;//防止以前统计过,置为1
pre=tem;
}
// cout<<"wewe"<<": "<<tem<<endl;

}
//会少比一次嘞,内循环比完后,还剩最后一次的值没有描述
//s[i]+="123";
s[i]+=to_string(index[pre])+to_string(pre);
// cout<<"出现次数: "<<index[pre]<<",对应值 "<<pre<<endl;
//下标置0,为了下一次循环
// index.clear();//删不掉

}
}
int main()
{
//s.push_back("11");没效果?
s[0]="1";
s[1]="11";
//s[2]="21"
//s[3]="1211"
//s[4]="111221"
//s[5]="312211"
//s[6]=
// bianli(1);
//cout<<s[0]<<" "<<s[1];
bianli(6);
// cout<<s[3];
cout<<s[6];
return 0;
}

10.考勤信息

题目描述

公司用一个字符串来表示员工的出勤信息

absent:缺勤

late:迟到

leaveearly:早退

present:正常上班

现需根据员工出勤信息,判断本次是否能获得出勤奖,能获得出勤奖的条件如下:

缺勤不超过一次;

没有连续的迟到/早退;

任意连续7次考勤,缺勤/迟到/早退不超过3次。

输入描述

用户的考勤数据字符串,记录条数 >= 1;

输入字符串长度 < 10000;

不存在非法输入

如:

2

present

present absent present present leaveearly present absent

输出描述

根据考勤数据字符串,如果能得到考勤奖,输出”true”;否则输出”false”,

对于输入示例的结果应为:

true false

输入

2

present

present present

输出

true

true

输入

2

present

present absent present present leaveearly present absent

输出

true

false

个人代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include<iostream>
#include<vector>
#include<string>
#include<sstream>
using namespace std;
void neibuchuli(vector<string> &s)//把空格去掉
{
string st=s[0];//实际是s[i][0]
string token;
stringstream ss(st);
while(getline(ss,token,' '))
{
s.push_back(token);//以后不用s[0]了,从1开始。
}
}
//absent late leaveearly 前三个不超过三次 present

/*缺勤不超过一次;

没有连续的迟到/早退;

任意连续7次考勤,缺勤/迟到/早退不超过3次。
*/
void solution(vector<string> s)
{
int queqin=0,chidao=0,zaotui=0;
for(int i=1;i<s.size();i++)
{
//一开始退出语句在这里,不对
// switch()
if(s[i]=="absent")
{
queqin++;
// cout<<i<<" ";
}
if(s[i]=="late") chidao++;
if(s[i]=="leaveearly") zaotui++;

// cout<<"i: "<<i<<" queqin:"<<queqin<<" late: "<<chidao<<" leaveearly: "<<zaotui<<endl;
//条件不能放在前面
//缺勤不能超过1次
if(queqin>1)
{
cout<<"false"<<endl;
return;
}
//连续的迟到或早退
if(i>1&&(s[i]=="late"||s[i]=="leaveearly"))
{
if(s[i]==s[i-1])
{
cout<<"false"<<endl;
return;
}
}
//缺勤/迟到/早退不超过三次
if(queqin+chidao+zaotui>3)
{
cout<<"false"<<endl;
return;
}

}
cout<<"true"<<endl;
}
int main()
{
int num;
cin>>num;
vector<string> s[num];

getchar();
string sw;
for(int i=0;i<num;i++)
{
getline(cin,sw); //回车为分割点
s[i].push_back(sw);
}
// for(string list:s[0])
// cout<<list<<" "<<endl;
// for(string list:s[1])
// cout<<list<<" "<<endl;
// cout<s[0].size()<<" "<<s[0][0].size();
for(int j=0;j<num;j++)
{
neibuchuli(s[j]);
}
// for(string list:s[1])
// cout<<list<<" "<<endl;
// for(string s11:s[0])
// cout<<s11<<"!@#@!#"<<endl;
// cout<<s[0].size();
for(int k=0;k<num;k++)
{
solution(s[k]);
}
// for(string list:s[1])
// {
// if(list=="absent")
// cout<<list<<endl;
// }
}

分析

1.以回车为分割,获取字符串

用到getline(),vector<string>

2.内部的空格分割,再追加字符串

用到stringstream,getline,push_back()

3.循环判断,标志++

细节

vector<int> s;

不知道大小,加值就push_back

知道大小,则vector<int> s(10);

可以s[0]=1;直接赋值

11.区间反转文章

题目描述

给定一段英文文章片段,由若干单词组成,单词间以空格间隔,单词下标从零开始,请翻转片段中指定区间的单词顺序并返回翻转后的内容。

例如给定的英文文章片段为“I am a developer.”,

翻转区间为[0,3],

则输出”developer. a am l”。

输入描述

使用换行隔开三个参数,第一个参数为英文文章内容即英文字符串,第二个参数为待翻转内容起始单词下标,第三个参数为待翻转内容最后一个单词下标。

输出描述

翻转后的英文文章片段,所有单词之间以一个半角空格分隔符进行输出

示例1

输入
I am a developer
1
2
输出
I a am developer

示例2

输入
hello world
-1
1
输出
world hello

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<sstream>
using namespace std;

void solution1(vector<string> &s,int begin,int end)
{

string sum="";
vector<string> sw;

for(int i=begin;i<=end;i++)
{

sw.push_back(s[i]);
}
// cout<<sw.size();
reverse(sw.begin(),sw.end());
// for(string list:sw)
// {
// cout<<list<<" ";
// }
for(int j=begin;j<=end;j++)
{

//哦,对于sw来说是从0开始的
// cout<<s[j]<<" "<<sw[j-begin]<<endl;
s[j]=sw[j-begin];
}
// cout<<s.size();
for(int k=0;k<s.size();k++)
{
//cout<<s[k];
if(k!=s.size()-1)
sum+=s[k]+" ";
else sum+=s[k];
}

cout<<sum;
}
void solution2(vector<string> &s,int l,int r)
{
while(l<r)
{
string tem=s[l];
s[l]=s[r];
s[r]=tem;
l++;
r--;
}
for(int k=0;k<s.size();k++)
{
//cout<<s[k];
if(k!=s.size()-1)
cout<<s[k]<<" ";
else cout<<s[k];
}
}
int main()
{
vector<string> s;
string t,token;
//cin>>t;
// getchar();
getline(cin,t);
// getchar();
stringstream ss(t);
while(getline(ss,token,' '))
{
s.push_back(token);
}
// for(string sqq:s)
// cout<<sqq<<",";
int begin,end;//单词下标位置
cin>>begin>>end;
// solution1(s,begin,end);
solution2(s,begin,end);


}

solution1是自己的方法,solution2是大佬HANWEN KE的wp

12.最大括号深度

现有一字符串 仅由'(',')','{','}','[',']'六种括号组成
若字符串满足以下条件之一,则为无效字符串

  1. 任意类型的左右括号数量不相等
  2. 存在未按正确顺序(先左后右)闭合的括号,
    输出括号的最大嵌套深度
    若字符串无效则输出0
    0 <= 字符串长度 <= 100000

输入描述

一个只包括'(',')','{','}','[',']'的字符串

输出描述

一个整数 ,最大的括号深度

示例一

输入

1
[]

Copy

输出

1
1

Copy

说明

有效字符串最大嵌套深度为1

示例二

输入

1
([]{()})

Copy

输出

1
3

个人代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/*
一字符串仅由三种(6个)括号组成,
求嵌套深度,若不合法,输出0
*/
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<sstream>
using namespace std;
void solution(string s)
{
vector<char> t;
int depth=0;
//3.为了防止}[]()这种情况,所以必须先入首字符
if(s[0]!='('&&s[0]!='['&&(s[0]!='{'))
{
cout<<0;
return;
}

for(int i=0;i<s.size();i++)
{
if((s[i]=='(')||(s[i]=='[')||(s[i]=='{'))
{
t.push_back(s[i]);
//depth=max(depth,t.size()); //报错?
if(depth<t.size())
depth=t.size();
}
if(i>0)
{
if(s[i]==')'&&t[t.size()-1]=='(')
{
t.pop_back();//1.哦,pop了之后,t的size改变了,进而索引变了,所以不能t[i-1]
// depth=t.size();
}
if(s[i]==']'&&t[t.size()-1]=='[')
{
t.pop_back();
// depth++;
}
if(s[i]=='}'&&t[t.size()-1]=='{')
{
t.pop_back();
// depth++;
}
}

}
//2.求最大嵌套深度,不是有效括号个数,那就是t存储个数
if(t.size()!=0) cout<<0;
else cout<<depth;
}
int main()
{
string s;
cin>>s;
solution(s);
}

思想

栈的利用,括号匹配

13.字符串加密

给你一串未加密的字符串str
通过对字符串的每一个字母进行改变来实现加密,
加密方式是在每一个字母str[i]偏移特定数组元素a[i]的量,
数组a前三位已经赋值:a[0]=1,a[1]=2,a[2]=4
i>=3时,数组元素a[i]=a[i-1]+a[i-2]+a[i-3]
例如:原文 abcde 加密后 bdgkr,其中偏移量分别是1,2,4,7,13

输入描述

第一行为一个整数n1 <= n <= 1000),
表示有n组测试数据,每组数据包含一行,
原文str(只含有小写字母, 0 < 长度 <= 50)。

输出描述

每组测试数据输出一行,表示字符串的密文

示例一

输入

1
2
1
xy

输出

1
ya

个人代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<iostream>
#include<vector>
using namespace std;
void pianyi(vector<int> &a,int n)
{
a[0]=1;
a[1]=2;
a[2]=4;
for(int i=3;i<n;i++)
{
a[i]=a[i-1]+a[i-2]+a[i-3];
// cout<<a[i]<<" ";
}
}
void solution(string &s,vector<int> a)
{
for(int i=0;i<s.size();i++)
{
s[i]=(s[i]-'a'+a[i])%26+'a';
}
cout<<s<<endl;
}
int main()
{
// string s;
// cin>>s;
// vector<int> a(s.size());
// pianyi(a,s.size());//算出从0到s.size-1的偏移量
// solution(s,a);
int n=0;
cin>>n;
getchar();
vector<string> s;
string t;
for(int i=0;i<n;i++)
{
getline(cin,t);
s.push_back(t);
}

// for(string list:s)
// cout<<list<<" ";
int len=0;
for(string list:s)
{
//len=max(len,list.size());
if(len<list.size())
len=list.size();
}


vector<int> a(len);
pianyi(a,len);//算出从0到s.size-1的偏移量
for(int i=0;i<s.size();i++)
{
solution(s[i],a);
}

}

也可用递归函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function fn(num){
if(num==0){
return 1
}
else if(num==1){
return 2
}
else if(num==2){
return 4
}
else{
return fn(num-1)+fn(num-2)+fn(num-3)
}
}

14.整数对最小和

题目描述

给定两个整数数组 array1 array2数组元素按升序排列
假设从array1 array2中分别取出一个元素可构成一对元素
现在需要取出K对元素
并对取出的所有元素求和
计算和的最小值
注意:
两对元素如果对应于array1 array2中的两个下标均相同,则视为同一个元素

输入描述

输入两行数组array1 array2
每行首个数字为数组大小 size( 0 < size <= 100)
0 < array1(i) <= 10000 < array2(i) <= 1000
接下来一行为正整数k (0 < k <= array1.size() * array2.size())

输出描述

满足要求的最小和

示例一

输入

1
2
3
3 1 1 2
3 1 2 3
2

输出

1
4

说明

用例中,需要取两个元素 取第一个数组第0个元素 与第二个数组第0个元素,组成一对元素[1,1]
取第一个数组第1个元素 与第二个数组第0个元素,组成一对元素[1,1]
求和为1+1+1+1=4 为满足要求的最小和

个人代码

1.各自排序,我用的快速排序

2.比较最小值相加即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include<iostream>
#include<vector>
using namespace std;
/*
3 1 1 2
3 1 2 3
2
*/
int part(vector<int> &a,int low,int high);
void quicksort(vector<int> &a,int low,int high);

int part(vector<int> &a,int low,int high)
{
int i=low,j=high,privot=a[low];//基准
while(i<j)
{
while(i<j&&a[j]>privot)//右边找一个小于privot的值
{
j--;
}
if(i<j)
{
swap(a[i++],a[j]);//交换完,i右移
}
while(i<j&&a[i]<=privot)//左边找一个大于privot的值
{
i++;
}
if(i<j)
{
swap(a[i],a[j--]);//交换完,j左移
}

}
return i;
}
void quicksort(vector<int> &a,int low,int high)
{
int mid;
if(low<high)
{
mid=part(a,low,high);//基准值
quicksort(a,low,mid-1);
quicksort(a,mid+1,high);
}
}
int main()
{
//a[0]取i个,则a[1]取k-i个;
//从a[0]求i个最小值和,从a[1]求k-i个最小值和

//排序,各自求最小值。双指针
vector<int> a[2];
int num;
for(int i=0;i<2;i++)
{
while(cin>>num)
{
a[i].push_back(num);
if(cin.get()=='\n') break;
}
// cin.ignore();
}
int k;
cin>>k;//这个是对数,即a[0][i]与a[1][j]所组成的对数

// quicksort(a[1],1,a[1][0]);
// for(int i:a[1])
// cout<<i<<" ";

//从小到大排序过后
//quicksort(a[0],1,a[0][0]); 为啥不行嘞
quicksort(a[0],1,a[0].size()-1);
quicksort(a[1],1,a[1].size()-1);
int i=1,j=1,co=0,sum=0;
//取的是整数"对",最小和,意味着有一方有最小的值,固定下来即可。
//逮住一个最小的薅,组成整数"对"
while(i<a[0][0]&&co<k)
{
if(a[0][i]<a[0][j])
{
sum+=a[0][i++]+a[0][j];
co++;
}
else
{
sum+=a[0][i]+a[0][j++];
co++;
}
}
cout<<sum;
}

wp

来自题目0002-整数对最小和 | AmosCloud Wiki

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Node {
int i, j, val;
};

bool cmp(Node a, Node b) {
return a.val < b.val;
}

int main() {
int m, n, k;
cin >> m;//a1数组的大小
vector<int> arr1(m);
for (int i = 0; i < m; i++) {
cin >> arr1[i];
}
cin >> n;//a2数组的大小
vector<int> arr2(n);
for (int i = 0; i < n; i++) {
cin >> arr2[i];
}
cin >> k;//整数对个数

vector<Node> nodes(m * n);
int nodesLen = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
nodes[nodesLen].i = i;
nodes[nodesLen].j = j;
nodes[nodesLen].val = arr1[i] + arr2[j];
nodesLen++;
}
}

sort(nodes.begin(), nodes.end(), cmp);//从小到大排序

int ans = 0;
vector<bool> used(m, false);
int cnt = 0;
for (int i = 0; i < nodesLen; i++) {
if (used[nodes[i].i]) continue;
ans += nodes[i].val;
used[nodes[i].i] = true;
cnt++;
if (cnt == k) break;
}

cout << ans << endl;

return 0;
}

15.求字符串中所有整数的最小和

题目描述

字符串s,只包含 a-z A-Z + - ;
合法的整数包括
1) 正整数 一个或者多个0-9组成,如 0 2 3 002 102
2)负整数 负号 - 开头,数字部分由一个或者多个0-9组成,如 -0 -012 -23 -00023

输入描述

包含数字的字符串

输出描述

所有整数的最小和

示例一

输入

bb1234aa

输出

10

个人代码

1.正数按个位加,负数按最大值减

2.用了stoi,字符串转化为数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include<iostream>
using namespace std;
void solution(string s)
{
int sum=0,flag=-1;
string t="";
for(char i:s)
{
if(i=='-')
{
flag=1;
continue;//跳过本次循环
}
if(flag>0) //负数处理
{
if(i>='0'&&i<='9')
{
t+=i;
}
else
{
sum-=stoi(t);
t="";
flag=-9;
}
}
else//正数处理
{
if(i>='0'&&i<='9')
{
sum+=i-'0';
}
}

}
cout<<sum;
}
int main()
{
string t;
cin>>t;
// cout<<'a'-'0'<<endl;
// cout<<'A'-'0'<<endl;
// cout<<'+'-'0'<<endl;
//所有整数的最小和,1234既可以看做是一个数1234,也可以看做4个数(1,2,3,4)。确保加和最小
//有减号,则-最大值。
//全是正整数,则按个位加和
//有正有减,个位正数加与最小负数相加
// string s1="00123",s2="120";
// cout<<stoi(s1);
solution(t);
}

16.乱序整数序列两数之和绝对值最小

题目描述

给定一个随机的整数数组(可能存在正整数和负整数)nums,
请你在该数组中找出两个数,其和的绝对值(|nums[x]+nums[y]|)为最小值
并返回这两个数(按从小到大返回)以及绝对值。
每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

输入描述

一个通过空格空格分割的有序整数序列字符串,最多1000个整数,
且整数数值范围是[-65535,65535]

输出描述

两个数和两数之和绝对值

示例一

输入

1
-1 -3 7 5 11 15

Copy

输出

1
-3 5 2

个人代码

双for

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include<iostream>
#include<algorithm>
using namespace std;
/*
一个通过空格空格分割的有序整数序列字符串,最多`1000`个整数,
且整数数值范围是`[-65535,65535]`

其和的绝对值`(|nums[x]+nums[y]|)`为最小值
并返回这两个数(按从小到大返回)以及绝对值。
*/
/*
-1 -3 7 5 11 15
-3 5 2
*/
//怎么说,非必要不同号
int main()
{

vector<int> s;
//输入是有序整数
int num;
while(cin>>num)
{
s.push_back(num);
if(cin.get()=='\n') break;
}

// for(int i:s)
// cout<<i<<",";
int min_num=1000,n1=s[0],n2=s[0];
for(int i=0;i<s.size();i++)
for(int j=i+1;j<s.size();j++)
{
if(min_num> abs(s[i]+s[j]))
{
min_num=abs(s[i]+s[j]);
n1=s[i];
n2=s[j];
}
}
cout<<n1<<" "<<n2<<" "<<min_num;
return 0;
}

17.非严格递增连续数字序列

题目

输入一个字符串仅包含大小写字母和数字
求字符串中包含的最长的非严格递增连续数字序列长度比如:
12234属于非严格递增数字序列

输入

输入一个字符串仅包含大小写字母和数字

输出

输出字符串中包含的最长的非严格递增连续数字序列长度

示例一

输入

abc2234019A334bc

输出

4

个人代码

当前值与前者比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include<iostream>
using namespace std;
//非严格递增长度 12234bs36912 长度5
int main()
{
//一个个遍历咯,
/*
是数字,且每次比前值相等或大一点,就co+1
否则,将最长值len更新,co清零,重新计算
*/
string t;
cin>>t;
char pre='0';
int begin=0,co=0,len=0;
for(int i=0;i<t.size();i++) //令pre存储第一个字符为数值
{
if(t[i]>='0'&&t[i]<='9')
{
pre=t[i];
begin=i;
break;
}
}

if(begin<t.size()) co++; //如果存在这样一个pre

for(int i=begin+1;i<t.size();i++)
{
if(t[i]>='0'&&t[i]<='9')//如果是数字
{
if(t[i]>=pre)//如果是非严格递增
{
pre=t[i];
co++;
}
else
{
len=max(len,co);
co=1;//置为1
}
}
else//如果不是数字
{
len=max(len,co);
}
}
cout<<len;
}

*18.分积木

题目描述

Solo和koko是两兄弟,妈妈给了他们一大堆积木,每块积木上都有自己的重量。现在他们想要将这些积木分成两堆。哥哥Solo负责分配,弟弟koko要求两个人获得
的积木总重量“相等”(根据Koko的逻辑),

个数可以不同,不然就会哭,

但koko只会先将两个数转成二进制再进行加法,而且总会忘记进位(每个进位都忘记)。

如当25(11001)加11(1011)时,koko得到的计算结果是18(10010):
11001
+01011

输入描述

3
3 5 6
第一行是一个整数N(2≤N≤100),表示有多少块积木;

第二行为空格分开的N个整数Ci(1≤Ci≤10^6),表示第i块积木的重量。

输出描述

11
让koko不哭,输出Solo所能获得积木的最大总重量;否则输出“NO”。

个人代码

参考了题目0107-分积木 | AmosCloud Wiki

其思想

1.如果该数组的值满足“可分配”条件,全部一起异或,最终值一定为0

2.关于最终结果为总和减去最小值

举个例子

3 5 6

最小值是3,同时减去3

0 2 3

全部异或,一定为0,暂时这样理解吧。不懂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include<iostream>
#include<vector>

using namespace std;
/*
两人获得积木总重量相等

计算方式,任意i个积木重量之和等于另外k-i个二进制相加(直接异或)的十进制之和
第一行为积木数
第二行为各积木重量
目的,分配重量相等
*/
int main()
{
// int a=25,b=11;
// cout<<(a^b);
int n;
cin>>n;
vector<int> a(n);
for(int i=0;i<n;i++)
cin>>a[i];

// for(int s:a)
// cout<<s<<",";
int solo=0,coco=0;
//这怎么搞呢,solo=coco,这些数怎么拼凑呢
//假如有5个数,保证两边个数可以不同,但和相同
//solo i个数 cooc n-i个数
//暴力解法怎么做?
//?????????????

//看了wp,其思路为相同的值异或为0
/*
该思想是异或到底,得到值为0,则成功
减去最小数字即可
*/
int num=a[0],sum=num,tmp=num;
for(int k=1;k<n;k++)
{
sum+=a[k];
num=min(num,a[k]);
tmp^=a[k];
}
if(tmp!=0) cout<<"NO";
else cout<<sum-num;

}

*19 连续字母长度

题目描述

给定一个字符串,只包含大写字母,求在包含同一字母的子串中,长度第 k 长的子串的长度,相同字母只取最长的那个子串。

输入描述

第一行有一个子串(1<长度<=100),只包含大写字母。
第二行为 k的值

输出描述

输出连续出现次数第k多的字母的次数。

示例

输入1

AAAAHHHBBCDHHHH
3

输出1

2

输入2

AABAAA
2

输出2

1

个人代码(不完整)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
/*
给定一个字符串,只包含大写字母,
求在包含同一字母的子串中,长度第 k 长的子串的长度,
相同字母只取最长的那个子串。

AAAAHHHBBCDHHHH
3
输出2
说明:同一字母最长的子串中,A为4,H为4(H为3去掉),B为2,C为1,D为1

AABAAA
2
输出1
说明:A取最长的为3,B为1
*/
using namespace std;

struct cmp
{
bool operator()(pair<char,int> &a,pair<char,int> &b)
{
return a.second>b.second;
}
};
int main()
{
/*
初始思路:需要统计相同字母的子串最大长度,还要排序。
1.那就map键值对,记录还要排个序,难点在于排序
2.直接vector,最后排序一下,难点在于相同字母不同长度的取舍:
那还要再额外建立一个vector标记A,B,C咯?

试试第一个吧
*/
string s;
cin>>s;
int n;
cin>>n;
map<char,int> mp;
mp[s[0]]=1;
char pre=s[0];
int co=1;//该值长度为1
for(int i=1;i<s.size();i++)
{
if(pre==s[i])
{
co++;
}
else//如果当前值与pre不等
{
if(mp.count(pre)) //如果有这个字符,更新一下最大值
{
if(mp[pre]<co)
mp[pre]=co;
}
else//没有就添加这个字符
{
//mp[pre]=1;
mp[s[i]]=1;
}
pre=s[i]; //更新一下pre
co=1;//置为1
}
}
for(pair<char,int> list:mp)
{
cout<<list.first<<","<<list.second<<endl;
}
vector< pair<char,int> >mp2(mp.begin(),mp.end());
//没有考虑不同字母,相同长度的情况
sort(mp2.begin(),mp2.end(),cmp());

// for(pair<char,int> list:mp2)
// cout<<list.first<<","<<list.second<<endl;

}

aaaaabbbccd
3
a,5
c,2

abcccdd
2
a,1
c,3

嘶,为什么会漏键值对呢。

近距离聚焦一波

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
for(int i=1;i<s.size();i++)
{
if(pre==s[i])
{
co++;
}
else//如果当前值与pre不等
{
if(mp.count(pre)) //如果有这个字符,更新一下最大值
{
if(mp[pre]<co)
mp[pre]=co;
}
else//没有就添加这个字符
{
//mp[pre]=1;
mp[s[i]]=1;
}
pre=s[i]; //更新一下pre
co=1;//置为1
}
}

更新后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
for(int i=1;i<s.size();i++)
{
if(pre==s[i])
{
co++;
}
else//如果当前值与pre不等
{
if(mp.count(pre)) //如果有这个字符,更新一下最大值
{
if(mp[pre]<co)
mp[pre]=co;
}
// else//没有就添加这个字符
// {
//mp[pre]=1;

mp[s[i]]=1; //并将未知值添加进来
// }
pre=s[i]; //更新一下pre
co=1;//置为1
}
}

abcccdd
2
a,1
b,1
c,3
d,1

怎么最后一个值没添上去嘞

当最后一个字符,是连续字符时,没有及时更新。

个人代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
/*
给定一个字符串,只包含大写字母,
求在包含同一字母的子串中,长度第 k 长的子串的长度,
相同字母只取最长的那个子串。

AAAAHHHBBCDHHHH
3
输出2
说明:同一字母最长的子串中,A为4,H为4(H为3去掉),B为2,C为1,D为1

AABAAA
2
输出1
说明:A取最长的为3,B为1
*/
using namespace std;

struct cmp
{
bool operator()(pair<char,int> &a,pair<char,int> &b)
{
return a.second>b.second;
}
};
int main()
{
/*
初始思路:需要统计相同字母的子串最大长度,还要排序。
1.那就map键值对,记录还要排个序,难点在于排序
2.直接vector,最后排序一下,难点在于相同字母不同长度的取舍:
那还要再额外建立一个vector标记A,B,C咯?

试试第一个吧
*/
string s;
cin>>s;
int n;
cin>>n;
map<char,int> mp;
mp[s[0]]=1;
char pre=s[0];
int co=1;//该值长度为1
for(int i=1;i<s.size();i++)
{
if(pre==s[i])
{
co++;
}
else//如果当前值与pre不等
{

//把前者更新
if(mp.count(pre)) //如果有这个字符,更新一下最大值
{
if(mp[pre]<co)
mp[pre]=co;
}
// else//没有就添加这个字符
// {
//mp[pre]=1;

//并将当前字符添加进来
mp[s[i]]=1; //
// }
pre=s[i]; //更新一下pre
co=1;//置为1
}
//最后如果是连续值,更新
if(mp.count(pre)) //如果有这个字符,更新一下最大值
{
if(mp[pre]<co)
mp[pre]=co;
}
}
// for(pair<char,int> list:mp)
// {
// cout<<list.first<<","<<list.second<<endl;
// }
vector< pair<char,int> >mp2(mp.begin(),mp.end());

//不同字母,相同长度的情况是并列排序的
sort(mp2.begin(),mp2.end(),cmp()); //map默认按键排序,自定义按值排序

// for(pair<char,int> list:mp2)
// cout<<list.first<<","<<list.second<<endl;

cout<<mp2[n-1].second;

}

标星知识点

map的键的唯一,(去重)

vector< pair<> >与map的关系

前值与当前值

*20.滑动窗口最大值

题目描述

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 。

示例

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值


[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

个人代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/*
给你一个整数数组 nums,
有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。
你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
*/
#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
#include<sstream>
using namespace std;
void bijiao(vector<int> a,int begin)
{
int maxn=a[begin];
for(int i=begin;i<a.size();i++)
maxn=max(maxn,a[i]);

cout<<maxn;
}
int main()
{
vector<int> nums;
//int num;
string s;
getline(cin,s);
stringstream ss(s);
string t;
while(getline(ss,t,','))
{
nums.push_back(stoi(t));
}
int k;
cin>>k;

vector<int> a;
//窗口大小为k,每次处理完,右移
//那就是第一次添加k个数
//之后每添加一个数就进行比较,输出
int co=0;
for(int i=0;i<nums.size();i++)
{
a.push_back(nums[i]);
co++;
if(co==k) break;
}
//1,3,-1,-3,5,3,6,7
co=k;
bijiao(a,0);
int i=1;
while(co<nums.size())
{
a.push_back(nums[co]);
cout<<" ";
bijiao(a,i);
i++;
co++;

}

return 0;
}

力扣代码(有误)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution {
public:
vector<int> b;
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> a;

//初始k个值
for(int i=0;i<k;i++)
a.push_back(nums[i]);

//对值进行比较
bijiao(a,0);
int i=1,co=k;
while(co<nums.size())
{
a.push_back(nums[co]);
bijiao(a,i);
i++;
co++;
}
return b;
}
void bijiao(vector<int> a,int begin)
{
int maxn=a[0]; //错误之处 =a[begin]即可
for(int i=begin;i<a.size();i++)
{
maxn=max(maxn,a[i]);
}
b.push_back(maxn);
}

};

嘶,按理说没毛病诶

针对[1,-1] 1

代码1是

1 -1

力扣是

1,1

针对[1,-2,0,4] 2

代码1是

1 0 4

力扣是

1 1 4

考虑是。。

哦,没事了,我在bijiao函数中的初始化不对,我标注了,不改了

嗯,力扣,不出所料地超时了

那就是双for的问题咯

1
2
3
4
5
6
7
8
9
int i=1,co=k;
while(co<nums.size())
{
a.push_back(nums[co]);
bijiao(a,i);
i++;
co++;
}
...

最终代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
#include<queue>
using namespace std;

int main()
{
vector<int> nums;
int num;
while(cin>>num)
{
nums.push_back(num);
if(cin.get()=='\n') break;
}
int k;
cin>>k;

deque<int> q;//双端队列
vector<int> a;
// for(int i=0;i<k;i++) //确保引进k个值,且末尾是最大的
// {
// while(!q.empty()&&q.back()<nums[i])
// {
// q.pop_back();
// }
// q.push_back(nums[i]);
// // a.push_back(q.front());
// }
//双端队列,用来保存单点递减队列,保持队首为最大值,为了确保窗口值,存储索引
for(int i=0;i<nums.size();i++)
{
while(!q.empty()&&nums[q.back()]<=nums[i])//如果不空且末尾值<=当前值
q.pop_back();//清除该值

q.push_back(i);//添加新下标

if(q.front()< i-k+1) //q里已经有k+1个元素,删除左边值
q.pop_front();
if(i+1>=k) //窗口形成
a.push_back(nums[q.front()]);
}
for(int list:a)
cout<<list<<",";

return 0;
}

标星知识点

超时

wp说是单调队列结构:为了可以同时弹出队首和队尾的元素,我们需要使用双端队列。满足这种单调性的双端队列一般称作「单调队列」。

删除比待加入的小的值,直到遇见更大的值

1.为什么用双端队列

数组5,3,4,1

第一次入队 5

第二次入队 5 3

第三次入队 5 4 出现在这里,一般队列是back入,front出(即队尾入,队头出);这里需要从back出。

第四次入队 5 4 1

C++中queue和deque的区别 - 知乎 (zhihu.com)

2.窗口值的维护

用队列存储索引的好处就是,可以判断窗口值大小,进而移动窗口

如:5 3 4 1 k=2

当入队了 5 3 判定当前值4

之后入队了是 5 4

因为4的索引为2,5的索引为0;i-k+1=2-2+1,即3的下标为1

弹出5

哎,这样吧,虽然队列里的值可能不会满足k个值,但是通过加进来的值的下标,我们可以将队首下标与临界值i-k+1比较,小于就弹出

3.窗口值已定

那就是当前下标值+1>=k

对于数组大小n,窗口m来说

1
2
3
4
5
6
7
for (int i = 0; i < n - m + 1; i++) {//决定窗口起始位置
int sum = 0;
for (int j = i; j < i + m; j++) {//决定了获取m个值
sum += integers.get(j);
}
if (sum > res) res = sum;
}

59.队列最大值

请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。

若队列为空,pop_front 和 max_value 需要返回 -1

示例 1:

输入:
[“MaxQueue”,”push_back”,”push_back”,”max_value”,”pop_front”,”max_value”]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]
示例 2:

输入:
[“MaxQueue”,”pop_front”,”max_value”]
[[],[],[]]
输出: [null,-1,-1]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class MaxQueue {
queue<int> que;
deque<int> deq;
public:
MaxQueue() { }
int max_value() {
return deq.empty() ? -1 : deq.front();
}
void push_back(int value) {
que.push(value);
while(!deq.empty() && deq.back() < value)
deq.pop_back();
deq.push_back(value);
}
int pop_front() {
if(que.empty()) return -1;
int val = que.front();
if(val == deq.front())
deq.pop_front();
que.pop();
return val;
}
};

作者:jyd
链接:https://leetcode.cn/problems/dui-lie-de-zui-da-zhi-lcof/solution/jian-zhi-offer-59-ii-dui-lie-de-zui-da-z-0pap/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

什么意思,为什么要一个queue+一个deque呢?

一个deque不就可以保持最大值吗。

举例,

[“MaxQueue”,”push_back”,”push_back”,”max_value”,”pop_front”,”max_value”]
[[],[1],[2],[],[],[]]

如果只有deque,为了维持最大值

输入 1:队列情况 1

输入2:队列情况 2 (1被清除了)

此时最大值为2

那么又执行了pop_front,2被清楚了

队列没值了

那就会输出-1

所以需要一个普通队列按顺序进队,以防pop操作时,deque队列仅删掉一个最大值就出现空队列的情况

你看 queue : 1 2 pop操作后,还有2

​ deque : 输入1 2 那就只有2(因为该队列的作用是维持最大值在队首) pop后,队列空了

而为什么一个queue不行,显而易见了

一个普通队列无法保留单调的最值问题。

21.素数之积

RSA加密算法在网络安全世界中无处不在,它利用了极大整数因数分解的困难度,数据越大,安全系数越高。

给定一个32位正整数,请对其进行因数分解,找出是哪两个素数的乘积。

输入描述

一个正整数num

0 < num <= 2147483647

输出描述

如果成功找到,以单个空格分割,从小到大输出两个素数,分解失败,请输出-1 -1。

示例1

输入输出示例仅供调试,后台判题数据一般不包含示例

输入

15

输出

3 5

个人代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include<iostream>

#include<vector>

#include<algorithm>

using namespace std;

/*

给定一个32位正整数,

请对其进行因数分解,找出是哪两个素数的乘积。

如果成功找到,以单个空格分割,

从小到大输出两个素数,分解失败,请输出-1 -1。

*/

bool issushu(int num)

{

for(int i=2;i<=sqrt(num);i++)

{

if(num%i==0) return 0;

}

return 1;

}

int main()

{

int num,tem=0;

cin>>num;

if(num<=1)

{

cout<<"-1 -1";

return 0;

}

for(int i=2;i<=sqrt(num);i++)

{

if(issushu(i)&&num%i==0)

{

tem=num/i;

}



if(issushu(tem))

{

cout<<i<<" "<<tem;

return 0;

}

}

cout<<"-1 -1";

return 0;

}

*22.仿LISP运算

题目描述

 LISP语言唯一的语法就是括号要配对。
 形如 (OP P1 P2 …),括号内元素由单个空格分割。
 其中第一个元素OP为操作符,后续元素均为其参数,参数个数取决于操作符类型
 注意:参数 P1, P2 也有可能是另外一个嵌套的 (OP P1 P2 …)
 当前OP类型为add/sub/mul/div(全小写),分别代表整数的加减乘除法。简单起见,OP参数个数为2

举例

  输入:(mul 3 -7)输出:-21
  输入:(add 1 2) 输出:3
  输入:(sub (mul 2 4) (div 9 3)) 输出 :5
  输入:(div 1 0) 输出:error
输入描述
  合法C字符串,字符串长度不超过512,用例保证了无语法错误
输出描述
  合法C字符串,字符包括’0’-‘9’及负号’-‘或者’error’

个人代码(有误,暂略)

双栈思想:操作码栈,参数栈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
stack<string> op;
stack<int> num;
void jisuan(stack<string> &op,stack<int> &nums,int n1,int n2)
{
string s=op.top();
// cout<<s<<endl;
op.pop();//弹出
switch(s[0]){ //说是只支持int或char
case 'a':
// return n1+n2;
nums.push(n1+n2);
break;
case 'm':
// return n1*n2;
nums.push(n1*n2);
break;
case 's':
// return n1-n2;
nums.push(n1-n2);
break;
case 'd':
// return n1/n2;
if(n2==0) cout<<"error";
else nums.push(n1/n2);
break;
}
}
void solution(string s)
{
int index=0;//操作数下标
for(int i=0;i<s.size();i++)
{
//(sub (mul 256 45) (div 91 30))
if(s[i]=='(') //截取操作符
{
op.push(s.substr(i+1,3));//
// cout<<s.substr(i+1,3)<<endl;
i=i+4;//符号位后第一个参数前的空格
index=i+1;//符号位后第一个参数的起始位置。可能是负数,可能是两位以上
}
else if(s[i]==' ')
{
// index=i+1;//
if(index<i)
{
num.push(stoi(s.substr(index,i-index)));//起始位置与个数
//cout<<num.top();
i++;
index=i+1;//第二个参数的起始位置
}

// num.push_back(stoi(s[index]));//哦,如果是两位数,就不能这样写
}
else if(s[i]==')')
{
if(index<i)
{
num.push(stoi(s.substr(index, i - index)));
i++;
index = i + 1; // 参数起始位置
}
//括号闭合一次,就算一次
int n2=num.top();
num.pop();
int n1=num.top();
num.pop();
jisuan(op,num,n1,n2);
}

}
while(!op.empty())//操作符栈非空
{
int n2=num.top();
num.pop();
int n1=num.top();
num.pop();
jisuan(op,num,n1,n2);
}
cout<<num.top();
}
int main()
{
string s;
cin>>s;
//vector<char> kuohao;
solution(s);
// cout<<num.top();
while(!op.empty())
{
cout<<op.top()<<endl;
op.pop();
}
while(!num.empty())
{
cout<<num.top()<<endl;
num.pop();
}
}

栈里的数值没有写进去?

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
stack<int> num_stack;

// 操作符栈
stack<string> opera_stack;
void calc(int param_1, int param_2) {
string op = opera_stack.top();
opera_stack.pop();
if (op == "add") {
num_stack.push(param_1 + param_2);
} else if (op == "sub") {
num_stack.push(param_1 - param_2);
} else if (op == "mul") {
num_stack.push(param_1 * param_2);
} else {
if (param_2 == 0) {
cout << "error" << endl;
exit(0);
} else {
int res = param_1 / param_2;
if (param_1 % param_2 != 0) {
if (res < 0) {
res -= 1;
} else {
res += 1;
}
}
num_stack.push(res);
}
}
}

int solution_01() {
// 处理输入
string exp;
getline(cin, exp);

int mark = 0;
int param_1 = 0; // 参数1
int param_2 = 0; // 参数2

for (int i = 0; i < exp.length(); i++) {
string ch = exp.substr(i, 1);
if (ch == "(") {
opera_stack.push(exp.substr(i + 1, 3));
i += 4;
mark = i + 1;
} else if (ch == ")") {
if (mark < i) {
num_stack.push(stoi(exp.substr(mark, i - mark)));
i += 1;
mark = i + 1;
}
param_2 = num_stack.top();
num_stack.pop();
param_1 = num_stack.top();
num_stack.pop();
calc(param_1, param_2);
} else {
if (ch == " ") {
if (mark < i) {
num_stack.push(stoi(exp.substr(mark, i - mark)));
mark = i + 1;
}
}
}
}

while (!opera_stack.empty()) {
param_2 = num_stack.top();
num_stack.pop();
param_1 = num_stack.top();
num_stack.pop();
calc(param_1, param_2);
}

return num_stack.top();
}

int main() {
int ans = solution_01();
cout << ans << endl;
return 0;
}

*23.贪吃蛇

题目描述

贪吃蛇是一个经典游戏,蛇的身体由若干方格连接而成,身体随蛇头移动。蛇头触碰到食物时,蛇的长度会增加一格。蛇头和身体的任一方格或者游戏版图边界碰撞时,游戏结束。

下面让我们来完成贪吃蛇游戏的模拟:
给定一个NM的数组ar,代表NM个方格组成的版图,贪吃蛇每次移动一个方格。若ar[i][j]==’H’,表示该方可为贪吃蛇的起始位置;若ar[i][j]==’F’,表示该方格为食物,若ar[i][j]==’E’,表示该方格为空格。
贪吃蛇初始长度为1,初始移动方向为向左。输入为给定一系列贪吃蛇的移动操作,返回操作后蛇的长度,如果在操作执行完之前已经游戏结束,返回游戏结束时贪吃蛇的长度。
贪吃蛇移动、吃食物和碰撞处理的细节见下面图示:

图 1:截取了贪吃蛇移动的一个中间状态,H表示蛇头,F表示食物,数字为蛇身体各节的 编号,蛇为向左移动,此时蛇头和食物已经相邻。

图 2:蛇头向左移动一格,蛇头和食物重叠,注意此时食物的格子成为了新的蛇头,第 1节 身体移动到蛇头位置,第 2节身体移动到第 1节身体位置,以此类推,最后添加第 4节升 起到原来第 3节身体的位置。

图 3:蛇头继续向左移动一格,身体的各节按上述规则移动,此时蛇头已经和边界相邻,但 还未碰撞。

图 4:蛇头继续向左移动一格,此时蛇头已经超过边界,发生碰撞,游戏结束。

图 5和图 6给出一个蛇头和身体碰撞的例子,蛇为向上移动。图 5时蛇头和第 7节身体相 邻,但还未碰撞;图 6蛇头向上移动一格,此时蛇头和第 8节身体都移动到了原来第 7节 身体的位置,发生碰撞,游戏结束。

输入描述:

输入第 1行为空格分隔的字母,代表贪吃蛇的移动操作。字母取值为 U、D、L、R、G,其中U、D、L、R分别表示贪吃蛇往上、下、左、右转向,转向时贪吃蛇不移动,G表示贪吃蛇按当前的方向移动一格。用例保证输入的操作正确。

第 2行为空格分隔的两个数,指定为 N和 M,为数组的行和列数。余下 N行每行是空格分 隔的 M个字母。字母取值为 H、F和 E,H表示贪吃蛇的起始位置F表示食物E表示该 方格为空。用例保证有且只有一个 H,而 F和 E会有多个

输出描述:

输出一个数字为蛇的长度。
示 例:
输入
D G G
3 3
F F F
F F H
E F E
输出
1

说明:向下移动两格,第一步吃空,第二步越界,故长度为1。

代码

主要思路为:

更新路线:

​ 1.当遇到E时,即空格,删除尾巴,添加新头部 (维持长度不变)

​ 2.当遇到F时,添加头部 (添加长度)

​ 3.始终用头部进行“移动”判断E和F

返回所记录的有效坐标的个数,即为长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
int tanchishe(vector<char> s, vector<vector<char>> a, int n, int m)
{
// 找到起始位置
int x = 0, y = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
{
if (a[i][j] == 'H')
{
a[i][j] = 'E'; // 头移动变空格
x = i;
y = j;

break;
}
}

pair<int, int> head = make_pair(x, y); // 头部
cout << "头部坐标:" << head.first << "," << head.second << endl;
// vector<pair<int,int> > body{head};//初始化

deque<pair<int, int>> body = {head}; // 为了头部插入
char direction = 'L';
for (auto dir : s)
{
// pair<int,int> next; //1.不该在这个位置,第一次选择方向而不是G时,默认为0,0
if (dir == 'U')
direction = 'U';
if (dir == 'D')
direction = 'D';
if (dir == 'L')
direction = 'L';
if (dir == 'R')
direction = 'R';
if (dir == 'G')
{
pair<int, int> next;
switch (direction)
{
case 'U': // 上移
// x--;
next = make_pair(body[0].first - 1, body[0].second);
break;
case 'D':
// x++;
next = make_pair(body[0].first + 1, body[0].second);
cout << endl
<< next.first << "," << next.second << endl;
cout << endl
<< body.front().first << "," << body.front().second << endl;
break;
case 'L':
// y--;
next = make_pair(body[0].first, body[0].second - 1);
break;
case 'R':
// y++;
next = make_pair(body[0].first, body[0].second + 1);
break;

if (x < 0 || y < 0 || x > n - 1 || y > m - 1)
return body.size();

cout << endl;
// 尾巴碰撞检查
for (auto c : body)
{
if (c == next)
return body.size();
}
// if(found) return body.size();
// if(x==a[0]&&y==a[1]) return body.size();
// if()
if (a[next.first][next.second] == 'E') // 空格
{
// 更新身体,添加头部,去掉尾部
// body.push_back({x,y});
cout << "删除尾巴,添加头部:" << next.first << "," << next.second << endl;
body.pop_back();
body.push_front(next);
// body.insert(next,body.begin(),body.end());错误用法
}
else if (a[next.first][next.second] == 'F') // 吃了东西
{
// 1.有个问题
// 一开始的时候,没有执行G,deque里没有值,默认为0,0,相当于判定了0,0
// 如果真的是F
cout << "吃了东西" << next.first << "," << next.second << endl;
body.push_front(next);
a[next.first][next.second] = 'E';
}
cout << "body:" << endl;
for (auto c : body)
cout << "(" << c.first << "," << c.second << ") ";
}
}
}
return body.size();
}
int main()
{
vector<char> s;
char sw;
while (cin >> sw)
{
s.push_back(sw);
if (cin.get() == '\n')
break;
}

int n, m;
cin >> n >> m;
vector<vector<char>> a(n, vector<char>(m)); // a[n][m];
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
{
cin >> a[i][j];
}

cout << tanchishe(s, a, n, m);
// pair<int,int> de=make_pair(1,3);
// deque<pair<int,int> >s={de};
// cout<<s[0].first+1<<" "<<s[0].second;
// for(int i=0;i<n;i++)
// {
// for(int j=0;j<m;j++)
// {
// cout<<a[i][j]<<" ";
// }
// cout<<endl;
// }
}

*24.解密犯罪时间

警察在侦破一个案件时,得到了线人给出的可能犯罪时间,形如 “HH:MM” 表示的时刻。
根据警察和线人的约定,为了隐蔽,该时间是修改过的,解密规则为:利用当前出现过的数字,构造下一个距离当前时间最近的时刻,则该时间为可能的犯罪时间。每个出现数字都可以被无限次使用

输入描述:

形如HH:SS的字符串,表示原始输入

输出描述:

形如HH:SS的字符串,表示推理出来的犯罪时间

示例1

输入

18:52

输出

18:55

说明

利用数字1, 8, 5, 2构造出来的最近时刻是18:55,是3分钟之后。结果不是18:51因为这个时刻是18小时52分钟之后。

示例2

输入

23:59

输出

22:22

说明

利用数字2, 3, 5, 9构造出来的最近时刻是22:22。 答案一定是第二天的某一时刻,所以选择可构造的最小时刻为犯罪时间。

备注:

可以保证线人给定的字符串一定是合法的。例如,“01:35” 和 “11:08” 是合法的,“1:35” 和 “11:8” 是不合法的。
最近的时刻有可能在第二天

目的

也就是说是,拆分组合后的,最大又最接近的时间。

线人肯定是给的提前的时间,所以要预估接下来的时间

23:59,拆成[2,3,5,9],任意组合,第二天最大又最接近的时间为22:22,23:55呢?

个人代码(有误)

/*思路(错误思路,不符合用例)
正常情况下:组合遍历,结果为大于原时间中,最小的那个时间
特殊情况下:第二天的时间,小于又最接近原时间的时间
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
int main()
{

string s;
cin>>s;
map<char,int> mp;
for(int i=0;i<s.size();i++)
{
if(s[i]!=':')
{
if(!mp.count(s[i]))
{
mp[s[i]]=s[i]-'0';
}
}
}
//1124
map<string,string> hh,mm;
// map<char,int> mp;
// mp['1']='1'-'0';
// mp['2']='2'-'0';
// mp['3']='3'-'0';

// 构造可能的HH
for(pair<char,int>list1:mp)
for(pair<char,int>list2:mp)
{
string token=to_string(list1.second)+to_string(list2.second);
if(list1.second<2)
{

if(!hh.count(hh[token]))
hh[token]=token;
}
if(list1.second==2&&list2.second<4)
{
if(!hh.count(hh[token]))
hh[token]=token;
}
}
//构造可能的MM
for(pair<char,int>list1:mp)
for(pair<char,int>list2:mp)
{
string token=to_string(list1.second)+to_string(list2.second);
if(list1.second<7)
{
if(!mm.count(mm[token]))
mm[token]=token;
}
}
// for(pair<string,string> list:hh)
// cout<<list.first<<","<<list.second<<" "<<endl;
// cout<<"分钟里";
// for(pair<string,string> list:mm)
// cout<<list.first<<","<<list.second<<" "<<endl;

//看分钟里是否大于
// string s="11:23";
int fenzhong=stoi(s.substr(3,2));
int xiaoshi=stoi(s.substr(0,2));
// cout<<s.substr(3,2);
string h="",m="";
int flag=-1;
for(pair<string,string> list:mm)
{
if(stoi(list.second)>fenzhong)
{
m=list.second;
flag=1;
cout<<s.substr(0,2)+":"+m;
return 0;
}
}
//看小时里是否大于

for(pair<string,string> list:hh)
cout<<list.second<<endl;

if(flag<0)
{

for(pair<string,string> list:hh)
{
if(stoi(list.second)>xiaoshi)
{
h=list.second;
flag=1;
cout<<h+":"+s.substr(3,2);
return 0;
}
}

}


//那就是第二天的时间,这个时间是比输入值小但又是最接近的时间
//倒序找吧
if(flag<0)
{
for(auto it=mm.rbegin();it!=mm.rend();it++)
{
if(stoi(it->second)<fenzhong)
{
m=it->second;
flag=1;
cout<<s.substr(0,2)+":"+m;
return 0;
}
}
if(flag<0)
{
for(auto it=hh.rbegin();it!=hh.rend();it++)
{
if(stoi(it->second)<xiaoshi)
{
h=it->second;
flag=1;
cout<<h+":"+s.substr(3,2);
return 0;
}
}
}
}

// char s1=static_cast<char>(97);
// char s=1+'0';
// cout<<s;

return 0;

}

实现的目标:

先找分钟有没有满足的,即大于真实分钟

不通过

再找小时有没有满足的,即大于真实小时

也不通过

说明是第二天的了

找第二天最小的

输入23:59 真实时间是22:22 ,而不是23:53

第二天就是要找最小的,而不是最接近的了。

而且是小时和分钟都找最小的嘞

看了其他用例才知道

12:58得到15:11 这个时间确实比15:58接近。

而且,对于正常情况,小时和分钟要同时保证现在时间最接近 真实时间

不是单纯

那得计算了诶,想简单了。

h*60+m 保持差值最小吧

/思路(
正常情况下:结果为大于当前时间中,最小的那个真实时间,是*大于当前时间的

特殊情况下:第二天的时间,即最小的真实时间,是小于当前时间

能不能理解为,正常情况下:小时大于当前,分钟小于当前

​ 特殊情况下:都小哈?

*/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

int fenzhong=stoi(s.substr(3,2));
int xiaoshi=stoi(s.substr(0,2));
// cout<<s.substr(3,2);
string h="",m="";
int flag=-1;

//看小时里是否大于
for(pair<string,string>list:hh)
{
if(stoi(list.second)>xiaoshi)
{
flag=2;
h=list.second;
break;
}
}
if(flag==2)//如果有满足的小时,找满足的分钟
{
for(pair<string,string>list:mm)
{
if(stoi(list.second)<fenzhong)
{
flag=3;
m=list.second;
cout<<h+":"+m;
return 0;//结束了
}
}

}
if(flag<0)//不然就是第二天的了
{
for(pair<string,string>list:hh)
{
if(stoi(list.second)<xiaoshi)
{
//flag=2;
h=list.second;
break;
}
}
for(pair<string,string>list:mm)
{
if(stoi(list.second)<fenzhong)
{
// flag=3;
m=list.second;
cout<<h+":"+m;
return 0;//结束了
}
}
}


return 0;

}

哦豁

18:52
输出21:11

正确应该是18:55

那就是针对小时的筛选,要大于等于当前小时,

trueh*60+truem>curh*60+curm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for(pair<string,string>list:hh)
{
if(stoi(list.second)>=xiaoshi)
{
flag=2;
h=list.second;
break;
}
}
if(flag==2)//如果有满足的小时,找满足的分钟
{
for(pair<string,string>list:mm)
{
if(stoi(h)*60+stoi(list.second)>xiaoshi*60+fenzhong)
{
m=list.second;
cout<<h+":"+m;
return 0;//结束了
}
}

07:08 又没值了? 真实时间应该是08

因为逻辑是大于等于当前小时,真实小时用的是07.。。

还是要研判一下,小时,什么时候取相同的,什么时候取大一点的,什么时候取第二天的

取决于什么嘞。

个人代码(最终)

思想:

1.看在相同小时下,能否获得比当前分钟大的真实分钟

2.看在稍大的小时下,能否使得trueh*60+truem>curh*60+curm

3.看在第二天的时间下,是否有真实小时小于当前小时,真实分钟小于当前分钟的时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
int main()
{
/*思路1,
正常情况下:组合遍历,结果为大于原时间中,最小的那个时间
特殊情况下:第二天的时间,最小于的时间
*/
string s;
cin>>s;

if(s=="00:00")
{
cout<<"00:00";
return 0;
}

map<char,int> mp;
for(int i=0;i<s.size();i++)
{
if(s[i]!=':')
{
if(!mp.count(s[i]))
{
mp[s[i]]=s[i]-'0';
}
}
}

map<string,string> hh,mm;


// 构造可能的HH
for(pair<char,int>list1:mp)
for(pair<char,int>list2:mp)
{
string token=to_string(list1.second)+to_string(list2.second);
if(list1.second<2)
{

if(!hh.count(hh[token]))
hh[token]=token;
}
if(list1.second==2&&list2.second<4)
{
if(!hh.count(hh[token]))
hh[token]=token;
}
}
//构造可能的MM
for(pair<char,int>list1:mp)
for(pair<char,int>list2:mp)
{
string token=to_string(list1.second)+to_string(list2.second);
if(list1.second<7)
{
if(!mm.count(mm[token]))
mm[token]=token;
}
}

int fenzhong=stoi(s.substr(3,2));
int xiaoshi=stoi(s.substr(0,2));
// cout<<s.substr(3,2);
string h="",m="";
int flag=-1;

// for(pair<string,string>list:hh)
// cout<<list.second<<" ";

// for(pair<string,string>list:mm)
// cout<<list.second<<" ";


//先看仅凭分钟能否解决 (即真实小时相同时)
for(pair<string,string>list:mm)
{
{
if(stoi(list.second)>fenzhong)
{
m=list.second;
cout<<s.substr(0,2)+":"+m;
return 0;//结束了
}
}
}
//看小时里是否大于
for(pair<string,string>list:hh)
{
if(stoi(list.second)>xiaoshi)
{
flag=2;
h=list.second;
break;
}
}
// cout<<h;
int diertian=-1;
if(flag<0)//没有满足的小时,那就是第二天的小时了,要小于
{
for(pair<string,string>list:hh)
{
if(stoi(list.second)<xiaoshi)//取个小的
{
flag=2;
diertian=1;
h=list.second;
break;
}
}
}

if(flag==2)//如果有满足的小时,找满足的分钟
{
for(pair<string,string>list:mm)
{
if(diertian<0) //不用到第二天
{
if(stoi(h)*60+stoi(list.second)>xiaoshi*60+fenzhong)
{
m=list.second;
cout<<h+":"+m;
return 0;//结束了
}
}
else //第二天
{
if(stoi(list.second)<=fenzhong)
{
m=list.second;
cout<<h+":"+m;
return 0;//结束了
}
}

}

}

return 0;

}

测试样例:

20:12得到20:20

23:59得到22:22

12:58得到15:11

18:52得到18:55

23:52得到23:53

09:17得到09:19

07:08得到08:00

噢,这个00:00,那就单独列出来输出00:00吧

25求满足条件的最长子串的长度

给定一个字符串,只包含字母和数字,按要求找出字符串中的最长(连续)子串的长度,字符串本身是其最长的子串,子串要求:
1、 只包含1个字母(az, AZ),其余必须是数字;
2、 字母可以在子串中的任意位置;
如果找不到满足要求的子串,如全是字母或全是数字,则返回-1。
输入描述:字符串(只包含字母和数字)
输出描述:子串的长度

示例

个人代码

输入 abC124ACb

输出 4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    string s;
cin>>s;

int flag=0,len=0,zimu,shuzi;//flag,子串中是否已经有一个字母了
string token="";
for(int i=0;i<s.size();i++)
{
if(s[i]<'0'||s[i]>'9')//非数字
{
zimu++;
if(flag==0)//子串没有字母的话
{
token+=s[i]; //
flag=1;
}
else//如果遇到新的字母了,将之前的len更新,重置
{
len=max(len,int(token.size()));
token="";
flag=0;
}
}
else
{
shuzi++;
token+=s[i];
len=max(len,int(token.size()));
}
}
//cout<<token;
//全是字母 or 全是数字则为-1
if(zimu==token.size()||shuzi==token.size()) cout<<-1;
else cout<<len;
}

*26.机器人走迷宫

1、房间由X*Y的方格组成,例如下图为6*4的大小。每一个方格以坐标(x,y)描述。
2、机器人固定从方格(0,0)出发,只能向东或者向北前进。出口固定为房间的最东角,如下图的方格(5,3)。用例保证机器人可以从入口走到出口。
3、房间有些方格是墙壁,如(4,1),机器人不能经过那儿。
4、有些地方是一旦到达就无法走到出口的,如标记为B的方格,称之为陷阱方格。
5、有些地方是机器人无法到达的的,如标记为A的方格,称之为不可达方格,不可达方格不包括墙壁所在的位置。
6、如下示例图中,陷阱方格有2个,不可达方格有3个。
7、请为该机器人实现路径规划功能:给定房间大小、墙壁位置,请计算出陷阱方格与不可达方格分别有多少个。

image-20230718181515105

输入

第一行为房间的x,y

第二行为房间墙壁个数n

N行墙壁的坐标

输入描述

  1. 第一行为房间的xy(0 < x,y <= 1000)
  2. 第二行为房间中墙壁的个数N (0 <= N < X*Y)
  3. 接着下面会有N行墙壁的坐标
    同一行中如果有多个数据以一个空格隔开,用例保证所有的输入数据均合法,(结尾不带回车换行)

输出描述

  1. 陷阱方格与不可达方格数量,两个信息在一行中输出,以一个空格隔开。(结尾不带回车换行)

示例一

输入

1
2
3
4
5
6
7
6 4
5
0 2
1 2
2 2
4 1
5 1

输出

1
2 3

方法

1.定义类o,接收x,y坐标

2.先接收wall坐标,o.x=wall[i][0],o.y=wall[i][1],并赋值给set<o> wallset

3.set<o> checks,接收所有可走的坐标

​ set<o> finish;接收到达终点坐标时的所经过的有效坐标

​ 通过递归函数实现值的输入findout(0,0,wallset,checks,finish)

4.不可达坐标数=x*y-checks.size()-wallset.size();

5.陷阱数计算:

​ 遍历finish

​ findout(finish.x,finish.y,wallset,checksT,finishT)

​ 如果在以finish.x和finish.y以起点的有效坐标中没找到可以到达终点的有效坐标,trap++

​ (if(!count(finishT.begin(),finishT.end(),终点坐标)))

**27.高效的任务规划

你有 n 台机器编号为 1~n,每台都需要完成完成一项工作,机器经过配置后都能完成独立完成一项工作。假设第 i 台机器你需要花 B 分钟进行设置,然后开始运行,J 分钟后完成任务。现在,你需要选择布置工作的顺序,使得用最短的时间完成所有工作。注意,不能同时对两台进行配置,但配置完成的机器们可以同时执行他们各自的工作。

输入描述:

第一行输入代表总共有 M 组任务数据(1 < M <= 10)。
每组数据第一行为一个整数指定机器的数量 N(0 < N <= 1000)。随后的 N 行每行两个整数,第一个表示B(0 <= B <= 10000),第二个表示 J(0 <= J <= 10000)。

每组数据连续输入,不会用空行分隔。各组任务单独计时。

输出描述:

对于每组任务,输出最短完成时间,且每组的结果独占一行。例如,两组任务就应该有两行输出。

示例 1

输入
1
1
2 2
输出
4

示例2

输入

2

2

1 1

2 2

3

1 1

2 2

3 3

输出

4

7

没读懂最短完成时间怎么来的

先配置工作时间最多的机器

当配置3,工作3的时候

可以顺便配置2,配置1

这时候只用了6分钟

那么在工作2和1并发的时候,只需要1分钟

总共7分钟

贪心思想:经典问题就是,找零钱和活动选择(尽可能最优)

参考wp(结果有误)

这种输入输出方法值得借鉴,用类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
struct task{
int tpei=0;
int tzuo=0;
task(){}
task(int x,int y):tpei(x),tzuo(y){}
};
class Solution{
public:
int taskfinishi(vector<task> &machine){
sort(machine.begin(),machine.end(),[](task &a,task &b){return a.tzuo>b.tzuo;});
int last=0;
int N=machine.size();
vector<int> dp(N);
for(int i=0;i<N;i++)//贪心
{
dp[i]=last+machine[i].tpei+machine[i].tzuo;
last+=machine[i].tpei;
}
int ans=0;
for(int i=0;i<N;i++)
{
ans=max(ans,dp[i]);
}
return ans;
}

};
int main()
{
int m,n;

cin>>m;//m组任务
// vector<int> s(n);
//先执行工作时间最长的
// for(int j=0;j<m;j++)
// {
// cin>>n;//第j组任务所需要机器数
// vector<int> a[n];//n行2列
// for(int i=0;i<n;i++)
// {
// int b1,b2;
// cin>>b1>>b2;
// a[i].push_back(b1);
// a[i].push_back(b2);
// }

// }
Solution a;
while(m--)
{
int n;
cin>>n;
vector<task> machine(n);
for(int i=0;i<n;i++)
{
cin>>machine[i].tpei>>machine[i].tzuo;
}
cout<<a.taskfinishi(machine)<<endl;
}


}

来自OceanStar的学习笔记

// [](task &a,task &b){return a.tzuo>b.tzuo;}) λ表达式

个人代码

参考高效任务规划

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
bool cmp(vector<int> &a,vector<int> &b)

{

// if(a[1]>b[1]) return true;

// else return false;



// if(a[1]>b[1]) return a>b;

// else return a<b;

return a[1]>b[1];

}

int main()

{

int m,n;

cin>>m;//m组任务

//vector<int> s(n);

// 先执行工作时间最长的

for(int j=0;j<m;j++)

{

cin>>n;//第j组任务所需要机器数

vector<vector<int> >a(n,vector<int>(2));//n行2列

for(int i=0;i<n;i++)

{

int b1,b2;

cin>>b1>>b2;

a[i][0]=b1;

a[i][1]=b2;

}

sort(a.begin(),a.end(),cmp);//先排序,工作时间长的在前

int time[1001]={},last=0,res=0;

for(int i=0;i<n;i++) //再贪心计算

{

time[i]=last+a[i][0]+a[i][1];

last+=a[i][0];

if(res<time[i]) res=time[i];

}

cout<<res<<endl;

}

return 0;


}




28.二叉树中序遍历

应该要正则的,感觉

题目描述

根据给定的二叉树结构描述字符串,输出该二叉树按照中序遍历结果字符串。中序遍历顺序为:左子树,根结点,右子树。

输入描述

由大小写字母、左右大括号、逗号组成的字符串:
1、字母代表一个节点值,左右括号内包含该节点的子节点
2、左右子节点使用逗号分隔,逗号前为空则表示左子节点为空,没有逗号则表示右子节点
为空
3、二叉树节点数最大不超过100。
注:输入字符串格式是正确的,无需考虑格式错误的情况。

输出描述

输出一个字符串,为二叉树中序遍历各节点值的拼接结果。

示例:

输入:a{b{d, e{g,h{,I}}},c{f}}
输出:dbgehiafc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
Treenode *build(Treenode *& node,string s)
{
int index=0;
// Treenode *root;//主节点
// Treenode *temp;//临时结点
// queue<Treenode* >stk;//保存根节点

// node=new Treenode(s[0]);//根节点
// temp=node;//指向该结点
// stk.push(temp);//存储该结点指针
node=new Treenode(s[0]);
index++;
queue<Treenode *> q;
q.push(node);
while(index<s.size())
{
Treenode *cur=q.front();
cout<<cur->val<<endl;
q.pop();


////a{b{d,e{g,h{,I}}},c{f}}

if(s[index]=='{')//
{
index++;//略过{
if(s[index]!=',')//{b
{
cur->left=new Treenode(s[index]);
q.push(cur->left);
}
else //{,i
{
index++;//略过,
cur->right=new Treenode(s[index]);
q.push(cur->right);
}
index++;
}
if(index<s.size()&&s[index]==',')//,e or ,i or ,c
{
index++;//略过,
cur->right=new Treenode(s[index]);
q.push(cur->right);
index++;

}
if(s[index]=='}')//}
{
index++;
}
}
//a{b{d, e{g,h{,I}}},c{f}}

return node;
}

void show(Treenode *node)
{
if(node==nullptr) return;

cout<<node->val<<" ";
show(node->left);
show(node->right);

}
int main()
{
string s;
cin>>s;
Treenode *root;
root=build(root,s);
show(root);


}

我也是之后才意识到错误,构建树,塔喵的一个空指针都没有。

知识点

关于树的构造

整棵树的构造=根节点+构造左子树+构造右子树

每一个结点都是其子树的根节点

654最大二叉树

给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:

创建一个根节点,其值为 nums 中的最大值。
递归地在最大值 左边 的 子数组前缀上 构建左子树。
递归地在最大值 右边 的 子数组后缀上 构建右子树。
返回 nums 构建的 最大二叉树 。

示例

1
2
输入:nums = [3,2,1,6,0,5]
输出:[6,3,5,null,2,0,null,null,1]

主要是确定根节点的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
return build(nums,0,nums.size()-1);
}
TreeNode * build(vector<int> nums,int lo,int hi)
{

if(lo>hi) return nullptr; //递归终止条件
int maxn=-1,index=0;
for(int i=lo;i<=hi;i++)//找最大值
{
if(maxn<nums[i])
{
maxn=nums[i];
index=i;
}
}
/*结构体定义了 TreeNode(int val):val(val),left(nullptr),right(nullptr){}*/
TreeNode*root=new TreeNode(maxn);

root->left=build(nums,lo,index-1);
root->right=build(nums,index+1,hi);

return root;
}

105 前序中序遍历结果构造二叉树

数据结构的手算方法都了解过,

第一次遍历

通过前序确定根节点,(根左右)

然后去中序找根节点,(左根右),通过该节点分左右子树,

在左右子树中再找根节点

问题是,之后的前序遍历,怎么确定根节点

那就是中序遍历结果,找到根节点之后捏,左边这一块区域的长度,即为leftsize

图来自labuladong

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return build(preorder,0,preorder.size()-1,inorder,0,inorder.size()-1);
}
TreeNode * build(vector<int> preorder,int lo1,int hi1,vector<int> inorder,int lo2,int hi2)
{
if(lo1>hi1) return nullptr;

int rootval=preorder[lo1];
//lo1++;
int index=0,leftsize=0;
for(int i=lo2;i<=hi2;i++)
{
if(rootval==inorder[i])
{
index=i;
break;
}

}
leftsize=index-lo2;

TreeNode *root=new TreeNode(rootval);

root->left=build(preorder,lo1+1,lo1+leftsize,inorder,lo2,index-1);
root->right=build(preorder,lo1+leftsize+1,hi1,inorder,index+1,hi2);
return root;
}

如果无重复元素,可以map记录索引值,避免了for循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
map<int,int> mp;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
for(int i=0;i<inorder.size();i++)
{
mp[inorder[i]]=i;//值与索引映射
}
return build(preorder,0,preorder.size()-1,inorder,0,inorder.size()-1);
}
TreeNode * build(vector<int> preorder,int lo1,int hi1,vector<int> inorder,int lo2,int hi2)
{
if(lo1>hi1) return nullptr;

int rootval=preorder[lo1];
//lo1++;
int index=mp[rootval];
int leftsize=index-lo2;
...
}

106中序后续确认二叉树

image-20230719170651578

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
map<int,int> mp;
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
for(int i=0;i<inorder.size();i++)
{
mp[inorder[i]]=i;
}
return build(inorder,0,inorder.size()-1,postorder,0,postorder.size()-1);
}

TreeNode * build(vector<int>inorder,int lo1,int hi1,vector<int>postorder,int lo2,int hi2)
{
if(lo1>hi1) return nullptr;

int rootval=postorder[hi2];
int index=mp[rootval];

int leftsize=index-lo1;//中序的根节点位置
TreeNode* root=new TreeNode(rootval);

root->left=build(inorder,lo1,index-1,postorder,lo2,lo2+leftsize-1);
//后序的左子树区间lo2到lo2+leftsize-1;
root->right=build(inorder,index+1,hi1,postorder,lo2+leftsize,hi2-1);
//后序的右子树区间lo2+leftsize到hi2-1;
return root;
}

29.书本叠放

题目描述

书籍的长宽都是整数对应(l, w)
如果书A的长宽度都比B长宽大时,
则允许将B排列放在A上面,
现在有一组规格的书籍,
书籍叠放时要求,书籍不能做旋转,
请计算最多能有多少个规格书籍能叠放在一起。

输入描述

  • 输入:books=[[20,16],[15,11],[10,10],[9,10]]
  • 说明:总共有4本书,第一本长度为20 宽度为16
    第一本长度为15 宽度为11
    以此类推
    最后一本书长度为9 宽度为10

输出描述

  • 输出:3
  • 说明: 最多三个规格的书籍可以叠放在一起 ,
    从下到上依次是[20,16],[15,11],[10,10]

示例一

输入

1
[[20,16],[15,11],[10,10],[9,10]]

Copy

输出

1
3

分析

主要是存储和比较,

存储嘛:要么用结构体,要么二维数组

比较方面:没有什么比得过sort函数了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
bool cmp(vector<int> &a,vector<int> &b)
{
return a[0]>=b[0]&&a[1]>=b[1];
}
int main()
{
//若A书的长与宽大于B书,则将B书放置A上,即长宽越小越在高处
vector<vector<int> >a(4,vector<int>(2));
a[3][0]=20,a[3][1]=16;
a[1][0]=15,a[1][1]=11;
a[2][0]=10,a[2][1]=10;
a[0][0]=9,a[0][1]=10;

sort(a.begin(),a.end(),cmp);//越大越靠前
int maxlon=99999,maxwid=99999,num=0;
for(auto c:a)
{
if(c[0]<maxlon&&c[1]<maxwid)
{
maxlon=c[0];
maxwid=c[1];
num++;
}
cout<<c[0]<<","<<c[1]<<endl;
}
cout<<num;
return 0;
}

正则用法

1
2
3
4
5
6
7
8
9
10
11
12
string s="[[20,16],[15,11],[10,10],[9,10]]";
string s1=s.substr(1,s.size()-2);//[20,16],[15,11],[10,10],[9,10]
regex regex("\\[([^,]+),([^\\]]+)\\]");

sregex_iterator iter(s1.begin(),s1.end(),regex);
sregex_iterator end;
while(iter!=end)
{
smatch match = *iter;
cout<<match[1]<<","<<match[2]<<endl;
iter++;
}
  1. \\[\\用于转义字符,[是正则表达式的特殊字符,所以使用\\[表示匹配一个左括号[
  2. ([^,]+):这是一个捕获组,()用于创建捕获组。[^,]+表示匹配一个或多个非逗号字符。换句话说,它匹配括号内的第一个逗号之前的所有字符,并将匹配结果保存在捕获组1中。
  3. ,:匹配一个逗号字符。
  4. ([^\\]]+):这也是一个捕获组,[^\\]]+表示匹配一个或多个非右括号字符。换句话说,它匹配括号内的第一个逗号之后到右括号之前的所有字符,并将匹配结果保存在捕获组2中。
  5. \\]:匹配一个右括号]

个人代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
struct book{
int len;
int wid;
// book(){}
};
bool cmp(book &a,book &b)
{
return a.len>=b.len&&a.wid>=b.wid;
}
int solution(vector<book> a)
{
int count=0,maxlen=9999999,maxwid=99999999;
for(book bk:a)
{
if (maxlen>bk.len&&maxwid>bk.wid)//讲究一个“都”字
{//10,10 与 9,10

maxlen=bk.len;
maxwid=bk.wid;
count++;
}
}
return count;
}
int main()
{
//若A书的长与宽大于B书,则将B书放置A上,即长宽越小越在高处

string s="[[20,16],[15,11],[9,10],[10,10]]";
string s1=s.substr(1,s.size()-2);//[20,16],[15,11],[9,10],[10,10]

stringstream ss(s1);
vector<string> list;
string token;
while(getline(ss,token,','))
{
list.push_back(token);
// cout<<token<<endl;
}
int n=list.size()/2; //4
// vector<vector<int> >a(n,vector<int>(2));
vector<book> bk(n);
for(int i=0;i<list.size();i+=2)
{
bk[i/2].len=stoi(list[i].substr(1));
bk[i/2].wid=stoi(list[i+1].substr(0,list[i+1].size()-1));
}
// for(int i=0;i<n;i++)
// cout<<bk[i].len<<","<<bk[i].wid<<endl;

//排序后
sort(bk.begin(),bk.end(),cmp);
cout<<solution(bk);


// for(int i=0;i<n;i++)
// cout<<bk[i].len<<","<<bk[i].wid<<endl;
// cout<<s1;
// sort(a.begin(),a.end(),cmp);//越大越靠前
// int maxlon=99999,maxwid=99999,num=0;
// for(auto c:a)
// {
// if(c[0]<maxlon&&c[1]<maxwid)
// {
// maxlon=c[0];
// maxwid=c[1];
// num++;
// }
// cout<<c[0]<<","<<c[1]<<endl;
// }
// cout<<num;
return 0;
}

***30.区间交集

给定一组闭区间,其中部分区间存在交集。任意两个给定区间的交集,称为公共区间(如:[1,2],[2,3]的公共区间为[2,2],[3,5],[3,6]的公共区间为[3,5])。公共区间之间 若存在交集,则需要合并(如:[1,3],[3,5]区间存在交集[3,3],需合并为[1,5])。按升序排列 输出合并后的区间列表。

输入描述

一组区间列表,区间数为 N: 0<=N<=1000;区间元素为 X: -10000<=X<=10000。

输出描述

升序排列的合并区间列表
备注:
1、区间元素均为数字,不考虑字母、符号等异常输入。
2、单个区间认定为无公共区间。

示例

输入
[[0, 3], [1, 3], [3, 5], [3, 6]]
输出
[[1, 5]]

个人代码

先找交集,能合并就合并,并输出最终交集

1.存储:用结构体

2.找交集:先按左端点排序,越小越前;

如何定义有交集,例如区间a,b;

若a的右端点大于等于b的左端点:说明a的左半边与b有重叠部分

若a的左端点小于等于b的右端点:说明a的右半边与b有重叠部分

两者一结合,满足重叠部分

3.交集合并,直接a,b两两比较,

如果a的右端点大于等于b的左端点

这个时候,右端点取决于这两个的最值//(哎,昏了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
struct qujian
{
int left;
int right;

bool operator<(const qujian &other) const{
return left<other.left||(left==other.left&&right<other.right); //对于set,需要自定义弱排序
}
};
bool cmp(qujian &a,qujian &b)
{
return a.left<b.left; //按开头排序
}

vector<qujian> jiaoji(vector<qujian> q,int n)//取交集
{

// vector<qujian> s;
set<qujian> quchong; //2.交集的重复性
//int index=0;
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
{
//if(q[i].left<=q[j].left&&q[i].right<=q[j].right) //
if(q[i].right>=q[j].left&&q[i].left<=q[j].right)//1.这才是包含在另一个区间的状况
{
qujian qu;
qu.left=max(q[i].left,q[j].left);
qu.right=min(q[i].right,q[j].right);
// index++;
quchong.insert(qu);
// s.push_back(qu);
}
}
vector<qujian> s(quchong.begin(),quchong.end());
return s;
}
vector<qujian> merge(vector<qujian> s)
{
qujian cur=s[0];
vector<qujian> wo;
for(int i=1;i<s.size();i++)
{
if(cur.right>=s[i].left)//至少说明从i的left到cur的right这部分有重叠
{
cur.right=max(cur.right,s[i].right);
}
else
{
wo.push_back(cur);
cur=s[i];
}
}
wo.push_back(cur);//最后的区间
return wo;
}
//合并公共区间
int main()
{
string s="[[0, 3], [1, 3], [3, 5], [3, 6]]";
string s1=s.substr(1,s.size()-2);


vector<string> n;
stringstream ss(s1);
string token;
while(getline(ss,token,','))
{
if(token[0]==' ')
token=token.substr(1); //去掉前面的空格
n.push_back(token);
// cout<<token<<endl;
}
int len=n.size()/2;

vector<qujian> a(len);
//存进值
for(int i=0;i<n.size();i+=2)
{

a[i/2].left=stoi(n[i].substr(1));
a[i/2].right=stoi(n[i+1].substr(0,n[i+1].size()-1));
}
sort(a.begin(),a.end(),cmp);

vector<qujian> we=merge(jiaoji(a,len));

for(auto c:we)
cout<<c.left<<","<<c.right<<endl;
cout<<endl;
//cout<<we.size();

return 0;
// cout<<stoi(" 123 ");//有空格是可以stoi转化的
}
1
2
3
4
5
6
7
8
9
cout<<"[";
string hebing="";
for(auto c:we)
hebing+="["+to_string(c.left)+", "+to_string(c.right)+"],";
hebing=hebing.substr(0,hebing.size()-1)+"]";
cout<<hebing;

输入 string s="[[0, 3], [1, 3], [3, 5], [3, 6], [6, 7]]";
输出 [[1, 5],[6, 6]]

知识点

1
2
3
bool operator<(const qujian &other) const{
return left<other.left||(left==other.left&&right<other.right); //对于set,需要自定义弱排序
}

有||的结果

1,3
3,3
3,5

没有||的结果

1,3
3,3

为什么要用这个弱排序,set为了保证键唯一,而qujian又是个自定义类型,需要自定义比较函数

operator<表示重定义小于,而const嘞,弱排序的硬性要求

1
2
3
4
5
6
qujian qu;
qu.left=max(q[i].left,q[j].left);
qu.right=min(q[i].right,q[j].right);
// index++;
quchong.insert(qu);
// s.push_back(qu);

这一块的妙用

[a,b]与[c,d]

判断重叠区间

a>=c&&b<=d

a<=d&&b>=c

56.合并区间

这道题只是合并,排好序后,更新右端为最大值即可

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:

输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:


vector<vector<int>> merge(vector<vector<int>>& intervals) {

sort(intervals.begin(),intervals.end());
vector<vector<int> > result;
vector<int> cur=intervals[0];

for(int i=1;i<intervals.size();i++)
{
if(cur[1]>=intervals[i][0])
{
cur[1]=max(cur[1],intervals[i][1]);
}
else
{
result.push_back(cur);
cur=intervals[i];
}
}
result.push_back(cur);
return result;
}
};

官方思想

1.按区间的左端点从小到大排序

2.对于交集的合并,左端点是相交区间最小的,右端点是相交区间最大的

986 区间的交集

给定两个由一些 闭区间 组成的列表,firstList 和 secondList ,其中 firstList[i] = [starti, endi] 而 secondList[j] = [startj, endj] 。每个区间列表都是成对 不相交 的,并且 已经排序 。

返回这 两个区间列表的交集 。

形式上,闭区间 [a, b](其中 a <= b)表示实数 x 的集合,而 a <= x <= b 。

两个闭区间的 交集 是一组实数,要么为空集,要么为闭区间。例如,[1, 3] 和 [2, 4] 的交集为 [2, 3] 。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/interval-list-intersections
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

1
2
输入:firstList = [[0,2],[5,10],[13,23],[24,25]], secondList = [[1,5],[8,12],[15,24],[25,26]]
输出:[[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]
1
2
输入:firstList = [[1,3],[5,9]], secondList = []
输出:[]
1
2
输入:firstList = [[1,7]], secondList = [[3,10]]
输出:[[3,7]]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    vector<vector<int>> intervalIntersection(vector<vector<int>>& firstList, vector<vector<int>>& secondList) {
//各自区间列表成对不相交,且排好序

int i=0,j=0;
vector<vector<int> >s;
while(i<firstList.size()&&j<secondList.size())
{
if(firstList[i][0]<=secondList[j][1]&&firstList[i][1]>=secondList[j][0])//公共区间满足公式
{
vector<int> a(2);
a[0]=max(firstList[i][0],secondList[j][0]);
a[1]=min(firstList[i][1],secondList[j][1]);
//i++;
//j++; //不是同时++,是[1,5]与[5,10]还有公共点
s.push_back(a);
}
if(firstList[i][1]>secondList[j][1]) j++;//如果f列表的i元素右端点比s列表的j元素的右端点大,说明j元素太窄了
else i++;
}
return s;
}
};

双for也能通过,只不过耗时点

关键在于理解重叠区间的逻辑。两个区间中有重叠区间,即左端点最大,右端点最小

1288 删除被覆盖区间

给你一个区间列表,请你删除列表中被其他区间所覆盖的区间。

只有当 c <= a 且 b <= d 时,我们才认为区间 [a,b) 被区间 [c,d) 覆盖。

在完成所有删除操作后,请你返回列表中剩余区间的数目。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/remove-covered-intervals
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

1
2
3
输入:intervals = [[1,4],[3,6],[2,8]]
输出:2
解释:区间 [3,6] 被区间 [2,8] 覆盖,所以它被删除了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 int removeCoveredIntervals(vector<vector<int>>& intervals) {
sort(intervals.begin(),intervals.end(),[](vector<int> &a,vector<int> &b){
if(a[0]==b[0]) return a[1]>b[1];
return a[0]<b[0];
});//lamda表达式,从小到大排序

vector<int> s=intervals[0];
int co=0;
for(int i=1;i<intervals.size();i++)
{
//重叠部分
if(s[1]>=intervals[i][1]&&s[0]<=intervals[i][0])
{
//intervals[i]包含于s
co++;
}
//有相交区间 [a,b]与[c,d] 即b>=c
if(s[1]>=intervals[i][0])
{
s[1]=max(s[1],intervals[i][1]);//更新
}
//不相交部分,则更新
if(s[1]<intervals[i][0])
{
s[0]=intervals[i][0];
s[1]=intervals[i][1];
}

}
return intervals.size()-co;

}
};

***31.分月饼

题目描述:

中秋节,公司分月饼,m个员工,买了n个月饼,m<=n,每个员工至少分1个月饼,但可以分多个,单人分到最多月饼的个数是Max1,单人分到第二多月饼个数是Max2,Max1-Max2 <= 3,单人分到第n-1多月饼个数是Max(n-1),单人分到第n多月饼个数是Max(n),Max(n-1) – Max(n) <= 3, 问有多少种分月饼的方法?

输入描述:

每一行输入m n,表示m个员工,n个月饼,m<=n

输出描述:

输出有多少种月饼分法

示例1:

输入

2 4

输出

2
————————————————
版权声明:本文为CSDN博主「晓佰阳孖」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41542580/article/details/127104240

wp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include<iostream>
#include<vector>
using namespace std;
vector<int> s;
int fenyuebing(int m,int n,int nmin)//剩余人数 月饼 最小月饼数
{
if(m<=0) return 0;
if(n<=0) return 0;
if(m==1) //就一个人了
{
if(n>=nmin&&n<=nmin+3)//题目要求了次大者与最大者之间的关系max(i)-max(i+1)<=3
{
return 1;
}
return 0;
}
int co=0;
for(int k=nmin;k<=nmin+3;k++)
{
//s.push_back(k);
cout<<co+1<<endl;
co+=fenyuebing(m-1,n-k,k);
// s.pop_back();
}
return co;
}
int main()
{
int m=2,n=4;//m<=n

int co=0;
for(int i=0;i<=n-m;i++)//已经分出去了m个月饼
{
// s.push_back(i);
co+=fenyuebing(m-1,n-i,i);//给m-1个人分n-i个月饼,最少分i个
// s.pop_back();
}
cout<<"总共"<<co;
return 0;
}

*32.找最小数

给一个正整数NUM1,计算出新正整数NUM2,NUM2为NUM1中移除N位数字后的结果,需要使得NUM2的值最小。 输入描述:
1.输入的第一行为一个字符串,字符串由0-9字符组成,记录正整数NUM1,NUM1长度小于32。
2.输入的第二行为需要移除的数字的个数,小于NUM1长度。
如:
2615371
4
输出描述:
输出一个数字字符串,记录最小值NUM2。
如:131
————————————————
版权声明:本文为CSDN博主「weixin_41934659」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_41934659/article/details/125888138

知识点

单调栈(双端队列实现)

维持一个递增队列,一旦遇到将入队的值比队尾,马上弹出队尾的值,同时统计删去的个数

额外处理:

1.如果删去的个数不够要求,继续删。

2.如果有前导0,处理一下,00456,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//目的,对s删掉n位后,不改变原位置,找到最小值
string s="261053718";
s="123245";
s="12300456";
int n=4;//小于s的个数
int len=s.size()-n;
deque<char> dq;
for(int i=0;i<s.size();i++)
{
while(!dq.empty()&&n>0&&dq.back()>s[i])//删除n位数字
{
dq.pop_back();//如果引进来的值小于队尾,弹出
n--;//维护了一个递增栈
}
dq.push_back(s[i]);
}
// for(auto c:dq)
// cout<<c<<" ";
for(int i=0;i<n;i++)//如果删除了m个数字,删除个数不够n个,继续删,比如123245,要求删4个,删除了一个3,即还有12245,继续删3个
{
dq.pop_back();
}

string result="";
bool iszero=true;
for(auto c1:dq)
{
if(iszero&&c1=='0') continue; //避免了前导0,比如261053718 删4位,最小的值是03718而不是10537
iszero=false;
result+=c1;

}
if(result.size()>0) cout<<result;
else cout<<"0";
return 0;

33.简易内存池

题目描述

有一个简易内存池,内存按照大小粒度分类,每个粒度有若干个可用内存资源。
用户会进行一系列内存申请,需要按需分配内存池中的资源,返回申请结果成功失败列表。
分配规则如下:

  1. 分配的内存要大于等于内存的申请量
    存在满足需求的内存就必须分配
    优先分配粒度小的,但内存不能拆分使用
  2. 需要按申请顺序分配
    先申请的先分配,有可用内存分配则申请结果为true
    没有可用则返回false
  • 注释:不考虑内存释放

输入描述

输入为两行字符串:
第一行为内存池资源列表,
包含内存粒度数据信息,粒度数据间用逗号分割,
一个粒度信息内用冒号分割,冒号前为内存粒度大小,冒号后为数量,
资源列表不大于1024
每个粒度的数量不大于4096
第二行为申请列表,
申请的内存大小间用逗号分割,申请列表不大于100000

64:2,128:1,32:4,1:128
50,36,64,128,127

输出描述

输出为内存池分配结果
true,true,true,false,false

说明

内存池资源包含:64k2个、128k1个、32k4个、1k128个的内存资源
针对50,36,64,128,127的内存申请序列,
分配的内存依次是,64,64,128,null,null
第三次申请内存时已经将128分配出去,因此输出的结果是
true,true,true,false,false

个人代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
int main()
{
string s,t;
cin>>s;
stringstream ss(s);
//map<int,int> mp;
vector<vector<int> > v;
while(getline(ss,t,','))
{
for(int i=0;i<t.size();i++)
{
if(t[i]==':')
{
vector<int> a(2);
a[0]=stoi(t.substr(0,i));
a[1]=stoi(t.substr(i+1));
v.push_back({a[0],a[1]});
}
}
}

string yaoqiu,to;
cin>>yaoqiu;
stringstream ss2(yaoqiu);
vector<int> arr;
while(getline(ss2,to,',')){
arr.push_back(stoi(to));
}

int i=0,j=0,flag=0;
while(i<arr.size())
{
flag=1;
for(int j=0;j<v.size();j++)
{
if(arr[i]<=v[j][0]&&v[j][1]>0)
{
v[j][1]--;
cout<<"true";
flag=-1;
break;
}
}
if(flag>0) cout<<"false";
if(i!=arr.size()-1) cout<<",";
i++;
}

return 0;
}

题中貌似说了优先最小粒度排序,那就用map吧,它自带键排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  int main()
{
string s,t;
cin>>s;
stringstream ss(s);
map<int,int> mp;
vector<vector<int> > v;
while(getline(ss,t,','))
{
for(int i=0;i<t.size();i++)
{
if(t[i]==':')
{
mp[stoi(t.substr(0,i))]=stoi(t.substr(i+1));
}
}
}

string yaoqiu,to;
cin>>yaoqiu;
stringstream ss2(yaoqiu);
vector<int> arr;
while(getline(ss2,to,',')){
arr.push_back(stoi(to));
}

int i=0,j=0,flag=0;
while(i<arr.size())
{
flag=1;
for(map<int,int>::iterator it=mp.begin();it!=mp.end();it++)
{
if(arr[i]<=it->first&&it->second>0)
{
it->second--;
cout<<"true ";
flag=-1;
break;
}
}
if(flag>0) cout<<"false ";
if(i!=arr.size()-1) cout<<",";
i++;
}

return 0;
}

*内存池2

题目描述

请实现一个简易内存池
根据请求命令完成内存分配和释放
内存池支持两种操作命令
REQUESTRELEASE其格式为
REQUEST=请求的内存大小
表示请求分配指定大小内存
如果分配成功,返回分配到的内存首地址
如果内存不足,或指定的大小为零则输出error
RELEASE=释放的内存首地址
表示释放掉之前分配的内存
释放成功无需输出
如果释放不存在的首地址
则输出error

注意:

  1. 内存池总大小为100字节
  2. 内存池地址分配必须是连续内存,并优先从低地址分配
  3. 内存释放后可被再次分配,已释放的内存在空闲时不能被二次释放
  4. 不会释放已申请的内存块的中间地址
  5. 释放操作只是针对首地址所对应的单个内存块进行操作,不会影响其他内存块

输入描述

首行为整数N
表示操作命令的个数
取值范围0<N<=100
接下来的N
每行将给出一个操作命令
操作命令和参数之间用”=“分割
输出描述见题目输出要求

输出描述

示例一

输入

1
2
3
2
REQUEST=10
REQUEST=20

Copy

输出

1
2
0
10

不知道怎么模拟0-100的内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
map<int,int>mp;
string request(int size);
bool release(int addr);
int main()
{
// vector<int> a(101);
int num=100;
int n;
cin>>n;
vector<string> s;
string t;
for(int i=0;i<n;i++)
{
cin>>t;
s.push_back(t);
}

// for(auto a:s)
// cout<<a<<endl;
for(int i=0;i<s.size();i++)
{
if(s[i][2]=='Q')
{
cout<<request(stoi(s[i].substr(8)))<<endl;
}
if(s[i][2]=='L')
{
bool res= release(stoi(s[i].substr(8)));
if(!res) cout<<"error";
}
}
return 0;
}
string request(int size)
{
int start=0,end=100;
int add1=start,add2=end;

if(size<=0||size>100) return "error";
//mp键存首地址,值为大小
if(mp.empty())
{
cout<<"add:"<<add1<<",size:"<<size<<endl;
mp[add1]=size;
}
else
{
vector<int> a;
//遍历vector获取mp的键
for(auto c:mp)
{
a.push_back(c.first);
}
//遍历vector
for(auto num:a)
{
//计算首地址的差值,如果>=要填的size
//更新mp
//否则就是该首地址被占用了
//更新首地址
if(num-add1>=size)
{
cout<<"num:"<<num<<"-add:"<<add1<<">=size:"<<size<<endl;
mp[add1]=size+add1;
cout<<"add:"<<add1<<",size:"<<size+add1<<endl;
}
else
{
cout<<"num:"<<num<<"-add:"<<add1<<"<size:"<<size<<endl;
add1=mp[num];
cout<<"update add:"<<add1<<endl;
}
}
if(size<=end-add1)
{
cout<<"size:"<<size<<"<="<<"end-add1:"<<end<<"-"<<add1<<endl;
mp[add1]=add1+size;
cout<<"add:"<<add1<<",size:"<<add1+size<<endl;
}
else return "error";
}
return to_string(add1);
}
bool release(int addr)
{
if(mp.count(addr))
{
mp.erase(addr);
return true;
}
return false;
}

5
REQUEST=10
REQUEST=20
RELEASE=0
REQUEST=20
REQUEST=10
add:0,size:10
0
num:0-add:0<size:20
update add:10
size:20<=end-add1:100-10
add:10,size:30
10
num:10-add:0<size:20
update add:30
size:20<=end-add1:100-30
add:30,size:50
30
num:10-add:0>=size:10
add:0,size:10
num:30-add:0>=size:10
add:0,size:10
size:10<=end-add1:100-0
add:0,size:10
0

34.服务失效

题目描述:

某系统中有众多服务,每个服务用字符串(只包含字母和数字,长度<=10)唯一标识,服务间可能有依赖关系,如A依赖B,则当B故障时导致A也故障。

依赖具有传递性,如A依赖B,B依赖C,当C故障时导致B故障,也导致A故障。

给出所有依赖关系,以及当前已知故障服务,要求输出所有正常服务。

依赖关系:服务1-服务2 表示“服务1”依赖“服务2”

不必考虑输入异常,用例保证:依赖关系列表、故障列表非空,且依赖关系数,故障服务数都不会超过3000,服务标识格式正常。

输入描述:

半角逗号分隔的依赖关系列表(换行)

半角逗号分隔的故障服务列表

输出描述:

依赖关系列表中提及的所有服务中可以正常工作的服务列表,用半角逗号分隔,按依赖关系列表中出现的次序排序。

特别的,没有正常节点输出单独一个半角逗号。

示例1

输入

a1-a2,a5-a6,a2-a3

a5,a2

输出

a6,a3

个人代码1(输出有序)

有个问题,因为用了map,所以输出时的顺序是按字典序排列的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
int main()
{
string s;
cin>>s;

stringstream ss(s);
string t;
vector<string> serve;
while(getline(ss,t,','))
{
serve.push_back(t);
}
vector<string> bad;
string t1;
cin>>t1;
stringstream ss1(t1);
string t2;
while(getline(ss1,t2,','))
bad.push_back(t2);

//如果是右边必坏,左边必坏,如果左边必坏,右边不坏
//搞一个bool记录下?

map<string,string> mp1;
map<string,bool> mp2;//记录哈
//vector<string>
for(auto c:serve)
{
for(int i=0;i<c.size();i++)
{
if(c[i]=='-')
{
// mp[c.substr(0,i)]=c.substr(0,i);
// mp[c.substr(i+1)]=c.substr(i+1);
mp1[c.substr(0,i)]=c.substr(i+1);
}
}

}
for(auto a:mp1) //提前记录一下
{
mp2[a.first]=true;
mp2[a.second]=true;
}
// for(auto a:bad)
// cout<<a<<",";
// cout<<endl;
// for(auto c:mp1)
// cout<<c.first<<","<<c.second<<endl;
//现在,干什么,先遍历bad,再遍历mp,如果是键=bad,如果是值=bad,对应bool false

// for(auto a:mp2)
// {
// cout<<a.first<<","<<a.second<<endl;
// }
for(auto a:bad)
for(auto c:mp1)
{
if(a==c.first&&mp2[c.first]==true)//等于左边,左边false
{
mp2[c.first]=false;
}
if(a==c.second&&mp2[c.second]==true)//等于右边,左右都false
{
mp2[c.first]=false;
mp2[c.second]=false;
}
}
int flag=-1;
// for(auto a:mp2)
// {
// cout<<a.first<<","<<a.second<<endl;
// }

int co=0;
for(auto a:mp2)
{
if(a.second==true)
{
flag=1;
cout<<a.first;
if(co!=mp2.size()-1) cout<<",";
}
co++;


}
if(flag<0) cout<<","<<endl;

return 0;
}

unorderd_map 是哈希表,塔喵的是胡乱排序

可是如果按照vector<pair<string,bool> >mp2,我到时候就不能直接mp2[键]这样访问了诶

个人代码2(按插入顺序输出)

思路是这样

1.首先定义两数组

1
2
vector<string> serve; //用于接收服务输入以逗号分割的值,如a1-a2
vector<string> bad;//用于接收故障输入,如a5

2.遍历serve数组,先筛选一遍,不是故障的服务(初次筛选)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
stringstream ss;
string t;
vector<string> chushai;
for(auto a:serve)
{
ss.str(a);
vector<string> temp;
while(getline(ss,t,'-'))
{
temp.push_back(t);
}
string a=temp[0];
string b=temp[1];

if(!count(bad.begin(),bad.end(),a)&&!count(chushai.begin(),chushai.end(),a))//既无故障,也不重复
{
chushai.push_back(a);
}
if(!count(bad.begin(),bad.end(),b)&&!count(chushai.begin(),chushai.end(),b))//既无故障,也不重复
{
chushai.push_back(b);
}

}

在遍历serve过程中,对每个元素值单独处理

(这一块不知道怎么描述,我觉得是关键之处)

(将一些待处理的值,比如”a1-a2 a5-a6…”,提前处理成”a1”,”a2”,”a5”,”a6”完成后,再对”a1”,”a2”,”a5”,”a6”统一进行下一步操作,如对比筛选)

不见得比下述方式好

(遍历的时候,先处理”a1-a2”,变成”a1”,”a2”,先不急着处理”a5-a6”,反而接着就”a1”,”a2”,先进行一遍筛选和处理)

然后这里的count()函数,需要头文件<algorithm>

3.进行二筛

1
2
3
4
5
6
vector<string> ershai;
for(auto a:chushai)
{
if(!isGZ(a))
ershai.push_back(a);
}

isGZ内容为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
bool isGZ(string s)
{
//它是故障
if(count(bad.begin(),bad.end(),s))
return true;

//它不是故障,看它的依赖是否是故障
for(auto item:mp)
{
if(item[0]==s&&isGZ(item[1])) return true;
}
//否则不是故障
return false;
}

这里的mp有门道,其定义为vector<vector<string> > mp;

类似于map的键值对的效果,但是呢,map是字典序排序的,不符合题意

所以在第2步,初筛的时候,我们需要将每次处理后的temp,还是要push_back到mp里,mp里,包含了依赖关系

所以加上一行 mp.push_back(temp);

反正ershai的元素即为最终输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

// Online IDE - Code Editor, Compiler, Interpreter

#include<iostream>
#include<vector>
#include<sstream>
#include<algorithm>
#include<unordered_map>
using namespace std;

vector<string> bad;//故障
vector<vector<string> >mp;//处理好的依赖关系
vector<string> serve; //待处理的依赖关系a1-a2

bool isGZ(string s)
{
if(count(bad.begin(),bad.end(),s)) //该服务故障
return true;//有故障

//如果这个服务没有故障,看下它的依赖
for(auto item:mp)
{
if(item[0]==s&&isGZ(item[1]))//左边是s,看下右边的依赖是否故障
return true;
}
return false;
}
int main()
{
string s;
cin>>s;

stringstream ss(s);
string t;

while(getline(ss,t,','))
{
serve.push_back(t);
}

string t1;
cin>>t1;
stringstream ss1(t1);
string t2;
while(getline(ss1,t2,','))
bad.push_back(t2);

vector<string> set1;//剔除了故障的服务
for(auto st:serve)
{
ss.clear();
ss.str(st);
vector<string> temp;
while(getline(ss,t1,'-'))
{
temp.push_back(t1);
}
mp.push_back(temp);
string a=temp[0];
string b=temp[1];

//故障列表里没有a
if(!count(bad.begin(),bad.end(),a)&&!count(set1.begin(),set1.end(),a))//bad里没有set1里也没有
{
set1.push_back(a);
}
//故障列表里没有b
if(!count(bad.begin(),bad.end(),b)&&!count(set1.begin(),set1.end(),b))
{
set1.push_back(b);
}
}
for(auto a:mp)
cout<<a[0]<<","<<a[1]<<endl;
for(auto a:set1)
cout<<a<<endl;
vector<string> listzc;//正常服务

//再筛选一遍
for(auto a:set1)
{
if(!isGZ(a))
listzc.push_back(a);
}
int len=listzc.size();
string res="";
if(len==0) cout<<",";
else
{
for(int i=0;i<len;i++)
{
res+=listzc[i];
if(i!=len-1) res+=",";
}
}
cout<<res<<endl;



return 0;
}

stringstream ss(t);

等价于

stringstream ss;

ss.str(t);

如果要重复利用ss,可以再声明之后,ss.clear();

35.图像物体的边界

给定一个二维数组M行N列,二维数组里的数字代表图片的像素,为了简化问题,仅包含像素1和5两种像素,每种像素代表一个物体,2个物体相邻的格子为边界,求像素1代表的物体的边界个数。
像素1代表的物体的边界指与像素5相邻的像素1的格子,边界相邻的属于同一个边界,相邻需要考虑8个方向(上,下,左,右,左上,左下,右上,右下)。
其他约束:
地图规格约束为:
0<M<100
0<N<100

1)如下图,与像素5的格子相邻的像素1的格子(0,0)、(0,1)、(0,2)、(1,0)、(1,2)、(2,0)、(2,1)、(2,2)、(4,4)、(4,5)、(5,4)为边界,另(0,0)、(0,1)、(0,2)、(1,0)、(1,2)、(2,0)、(2,1)、(2,2)相邻,为1个边界,(4,4)、(4,5)、(5,4)相邻,为1个边界,所以下图边界个数为2。

输入示例

6 6

1 1 1 1 1 1

1 5 1 1 1 1

1 1 1 1 1 1

1 1 1 1 1 1

1 1 1 1 1 1

1 1 1 1 1 5

输出

2

说明:就是1作为围墙,能把中间的5围死,加粗部分

输入描述:

  • 第一行,行数M,列数N 第二行开始,是M行N列的像素的二维数组,仅包含像素1和5

输出描述:

  • 像素1代表的物体的边界个数。如果没有边界输出0(比如只存在像素1,或者只存在像素5)。

个人代码

我就是笨办法

考虑情况为:

1.四个角落,需要判断3个值

2.除开四个角落的地方(第一行,最后一行,第一列,最后一列)考虑5个值

3.其余为8个值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

// Online IDE - Code Editor, Compiler, Interpreter

#include<iostream>
#include<vector>
#include<sstream>
#include<algorithm>
using namespace std;
int solution(vector<vector<int> >a,int n,int m)
{
int co=0;
vector<vector<int> >v;
int x=0,y=0,flag1=0,flag5=0;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
if(a[i][j]==5)
{
flag5=1;
vector<int> temp;
temp.push_back(i);
temp.push_back(j);
v.push_back(temp);
}
else flag1=1;
}

for(auto m:v)
cout<<m[0]<<" "<<m[1]<<endl;
if(flag1==0||flag5==0) return 0;
else
{
for(auto temp:v)
{
int p=temp[0],q=temp[1];
if(p==0) //第一行
{
if(q==0)//只要三个
{
if(a[0][1]==1&&a[1][1]==1&&a[1][0]==1) co++;
}
else if(q==m-1)//只要三个
{
if(a[0][m-2]==1&&a[1][m-2]==1&&a[1][m-1]==1) co++;
}
else //五个
{
if(a[0][q-1]==1&&a[0][q+1]==1&&a[1][q-1]==1&&a[1][q]==1&&a[1][q+1]==1) co++;
}

}
else if(q==0) //第一列
{

if(p==n-1)//只要三个
{
if(a[n-2][0]==1&&a[n-2][1]==1&&a[n-1][1]==1) co++;
}
else //五个
{
if(a[p-1][0]==1&&a[p+1][0]==1&&a[p-1][1]==1&&a[p][1]==1&&a[p+1][1]==1) co++;
}

}
else if(p==n-1)//最后一行
{
// if(q==0)//只要三个
if(q==m-1) //只要三个
{
if(a[p-1][q]==1&&a[p-1][q-1]==1&&a[p][q-1]==1) co++;
}
else //只要五个
{
if(a[p-1][q]==1&&a[p+1][q]==1&&a[p-1][q-1]==1&&a[p][q-1]==1&&a[p+1][q-1]==1) co++;
}

}
else if(q==m-1)//最后一行
{
if(a[p-1][q-1]==1&&a[p-1][q]==1&&a[p-1][q+1]==1&&a[p][q-1]==1&&a[p][q+1]==1) co++;

}
else //八个
{
if(a[p-1][q-1]==1&&a[p-1][q]==1&&a[p-1][q+1]==1&&a[p][q-1]==1&&a[p][q+1]==1&&a[p+1][q-1]==1&&a[p+1][q]==1&&a[p+1][q+1]==1)
co++;

}

}
}


return co;
}
int main()
{
int n,m;
cin>>n>>m;
/*
6 6

1 1 1 1 1 1

1 5 1 1 1 1

1 1 1 1 1 1

1 1 1 1 1 1

1 1 1 1 1 1

1 1 1 1 1 5

初步想法是先找5,然后探索它的八个方向是否都是1
*/
vector<vector<int> >a(n,vector<int>(m));
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>a[i][j];
cout<<solution(a,n,m);

return 0;
}

4 4
1 1 1 1
1 1 5 5
1 1 1 5
1 1 1 1
1 2
1 3
2 3
0

36.跳格子

地上共有N个格子,你需要跳完地上所有的格子,但是格子间是有强依赖关系的,跳完前一个格子后,后续的格子才会被开启,格子间的依赖关系由多组steps数组给出,steps[0]表示前一个格子,step[1]表示steps[0]可以开启的格子
比如[0,1]表示从跳完第0个格子以后第1个格子就开启了,比如[2,1],[2,3]表示跳完第2个格子后第1个格子和第3个格子就被开启了。
请你计算是否能由给出的steps数组跳完所有的格子,如果可以输出yes,否则输出no
说明:

1.你可以从一个格子调到任意一个开启的格子
2.没有前置依赖条件默认就是开启的
3.如果总数是N,则所有的格子编号为[0,1,2,3….N-1]l连续的数组
输入描述
输入一个整数N表示总共有多少个格子,接着输入多组二维数组steps表示所有格子之间的依赖关系。
输出描述
如果能按照steps给定的依赖顺序跳完所有的格子输出yes,否则输出no.

输入

1
2
3
3
0 1
0 2

输出

1
yes

个人代码(有误)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

// Online IDE - Code Editor, Compiler, Interpreter

#include<iostream>
#include<vector>
#include<sstream>
#include<algorithm>
#include<map>
using namespace std;
bool isbihuan(vector<vector<int> >&gezi,vector<int> &agezi)
{
for(auto temp:gezi)
{
if(temp[0]==agezi[agezi.size()-1])//
{
if(find(agezi.begin(),agezi.end(),temp[1])!=agezi.end())
{
return true;
}
}

agezi.push_back(temp[0]);
agezi.push_back(temp[1]);

if(isbihuan(gezi,agezi)) return true;
}
return false;
}

int main()
{

/*
根据示例来看,走完所有格子就是保持线性关系咯,如果闭环那就不可以咯
0 1
1 0 也就是没提,默认开启,比如单个0,1;但如果像这种0,1互依赖情况,
那么用一个vector去存储成对的依赖关系 vector<vector<int> >
另一个vector也存储依赖关系,但是保持在一行上vector<int>
遍历第一个vector,取它每个元素的第一个与第二个vector的最后一个元素去判断相等,若相等则闭环
*/
int n;
cin>>n;//格子数
cin.ignore();
vector<vector<int> >gezi;//依赖关系
// map<int,int> m;//
// map<int,int>mp;//计数
// int a,b;
string input,t;
while(1)
{
getline(cin,input);
if(input.empty()) break;//多输入一遍空格即可
// m[a]=a;
// m[b]=b;
stringstream ss(input);
vector<int> a;
while(getline(ss,t,' '))
{

a.push_back(stoi(t));
}
gezi.push_back(a);
// if(m.size()==n) break;

}
// for(auto a:m)
// cout<<a.first<<endl;
// for(auto a:gezi)
// cout<<a[0]<<","<<a[1]<<endl;

bool istrue=true;//可以成功
for(auto list:gezi)
{
if(isbihuan(gezi,list))
{
istrue=false;
break;
}
}
if(istrue) cout<<"yes";
else cout<<"no";
/*
0 1
0 2
*/

return 0;
}

wp

利用图及深度遍历

来自 GHHIAS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
enum status {unv,ving,ved};//遍历状态,不能用数字
void dfs(int node,vector<status>& visit,vector<vector<int> >& edges,bool & stepall)
{
visit[node]=ving;//正在遍历

for(int neighbor:edges[node])//遍历邻居节点
{
if(visit[neighbor]==unv)//没访问
{
dfs(neighbor,visit,edges,stepall);//递归遍历邻居节点

//能跳完所有格子
if(!stepall) return;
}

else if(visit[neighbor]==unv)//若邻居节点正在遍历,但未完成遍历
{
stepall=false;//无法跳完所有格子
return;
}
}
visit[node]=ved;//访问过,即遍历完成
}
int main()
{

int n;
cin>>n;

cin.ignore();

//初始化边
vector<vector<int> >edges(n);
//vector<status> visit(n,0);//enum不支持数字
vector<status> visit(n,unv);


bool stepall=false;
string s;
while(getline(cin,s))
{
if(s.empty()) break;

int a,b;
stringstream ss(s);
ss>>a>>b;//解析输入的两个数字
edges[b].push_back(a);//记录边
}
for(int i=0;i<n&&stepall;i++)//遍历所有节点
{
if(visit[i]==unv)//如果节点未被遍历
dfs(i,visit,edges,stepall);//从该结点开始遍历
}

cout<<(stepall?"yes":"no")<<endl;

return 0;
}

37.数组二叉树

二叉树也可以用数组来存储 给定一个数组

树的根节点的值储存在下标1

对于储存在下标n的节点,他的左子节点和右子节点分别储存在下标2n和2n+1

并且我们用-1代表一个节点为空

给定一个数组存储的二叉树

试求从根节点到最小的叶子节点的路径

路径由节点的值组成

输入描述

输入一行为数组的内容 数组的每个元素都是正整数,元素间用空格分割 注意第一个元素即为根节点的值 即数组的第n元素对应下标n 下标0在树的表示中没有使用 所以我们省略了 输入的树最多为7层

输出描述

输出从根节点到最小叶子节点的路径上各个节点的值 由空格分割 用例保证最小叶子节点只有一个

例子

输入

3 5 7 -1 -1 2 4

输出

3 7 2

下面两条wp的思路都是,先找到最小的叶结点,那么从根节点到它的路径就唯一确定了。

最小叶结点的要求,它首先不是-1,其值最小,它左孩子下标溢出

从它开始倒着找父节点就行了。

wp1

来自CSDN巨坚强

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 vector<int> a;
a.push_back(0);//先占用一个
int b;
while(cin>>b)
{
a.push_back(b);
if(cin.get()=='\n') break;
}
//先找到最小叶子结点,即i有值但2i和2i+1处无值
int min_1=99999999,min_index=1;
for(int i=1;i<a.size();i++)
{
int cur=a[i];

if(cur==-1||cur>min_1) continue;//当前节点无或比记录的值大
if(a[2*i]==-1&&a[2*i+1]==-1)//如果没有孩子
{
min_1=cur;
min_index=i;
}
else if(2*i>a.size())//或者如果左孩子下标超出
{
min_1=cur;
min_index=i;
}
// i=i+3;
}
// cout<<min_1<<","<<min_index<<","<<a[min_index];
//再逆序从叶子结点找到根节点
string t="";
while(min_index!=0)
{
t+=to_string(a[min_index]);
if(min_index!=1) t+=" ";
min_index/=2;
}
reverse(t.begin(),t.end());
cout<<t;

}

wp2

来自AmosCloud

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
void back(vector<int> a,int minpos,vector<int> &path);
void solution(vector<int> a);
int main()
{

vector<int> a;
a.push_back(0);//先占用一个
int b;
while(cin>>b)
{
a.push_back(b);
if(cin.get()=='\n') break;
}
solution(a);

}
void solution(vector<int> a)
{
int min_1=999999;
int minpos=1;
for(int i=1;i<a.size();i++)
{
int tmp=a[i];
if(tmp!=-1&&tmp<min_1&&2*i>a.size())//当前值不为-1,当前值较小,左孩子下标溢出
{
//叶子结点
min_1=tmp;
minpos=i;
}
}
vector<int> path;
back(a,minpos,path);
for(int i=path.size()-1;i>=0;i--)
{
cout<<path[i];
if(i!=0)
cout<<" ";
}

}
void back(vector<int> a,int minpos,vector<int> &path)
{
//从最后的叶子结点倒着找父节点,直到找到根节点
path.push_back(a[minpos]);
if(minpos==1) return;
if(minpos%2==0)//是左孩子,求其父节点
back(a,minpos/2,path);
else back(a,(minpos-1)/2,path);//是右孩子,求其父节点
}

好吧,不需要真的用结构体来做

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
struct tree
{
int val;
tree *left,*right;
tree():val(-1),left(nullptr),right(nullptr){}
tree(int x):val(x),left(nullptr),right(nullptr){}
};
vector<tree> node(99999);
void build(int index)
{
int a;
cin>>a;
if(cin.get()=='\n') return;
node[index]= tree(a);

build(2*index);
build(2*index+1);

}
int main()
{

int i=1;

string line;

build(i);

for(auto a:node)
cout<<a.val<<endl;


}

38.考古学家(全排列)

题目描述

有一个考古学家发现一个石碑
但是很可惜 发现时其已经断成多段
原地发现N个断口整齐的石碑碎片
为了破解石碑内容
考古学家希望有程序能帮忙计算复原后的石碑文字组合数

备注: 如果存在石碑碎片内容完全相同,则由于碎片间的顺序不影响复原后的碑文内容,
仅相同碎片间的位置变化不影响组合

输入描述

第一行输入NN表示石碑碎片的个数
第二行依次输入石碑碎片上的文字内容S共有N

输出描述

输出石碑文字的组合(按照升序排列),行尾无多余空格

示例一

输入

1
2
3
a b c

输出

1
2
3
4
5
6
abc
acb
bac
bca
cab
cba

个人代码

回溯算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
set<string> list;
string result="";
void back(vector<string> &s,int start,int end)
{
if(start==end)
{
for(auto a:s)
result+=a;
// cout<<a;

list.insert(result);
result="";
}
else
{
for(int i=start;i<=end;i++)
{
swap(s[start],s[i]);//第一次s[0]与s[0]
back(s,start+1,end);
swap(s[start],s[i]);//还原
}
}

}
int main()
{
//全排列
int n;
vector<string> s(n);
cin>>n;
//cin.ignore();
string t;
while(cin>>t)
{
s.push_back(t);
if(cin.get()=='\n') break; //所以是一行输完
}
// for(auto list:s)
// cout<<list<<",";
back(s,0,s.size()-1);
//排序
//去重 set
for(auto a:list)
cout<<a<<endl;


return 0;
}

39*解压报文

为了提升数据传输的效率,会对传输的报文进行压缩处理。输入一个压缩后的报文,请返回它解压后的原始报文。
压缩规则:n[str],表示方括号内部的 str 正好重复 n 次。注意 n 为正整数(0 < n <= 100),str只包含小写英文字母,不考虑异常情况。
“ “输入描述:
输入压缩后的报文:
1)不考虑无效的输入,报文没有额外的空格,方括号总是符合格式要求的;
2)原始报文不包含数字,所有的数字只表示重复的次数 n ,例如不会出现像 5b 或 3[8] 的输入;
输出描述:
解压后的原始报文
注:
1)原始报文长度不会超过1000,不考虑异常的情况
示例1
输入
3[k]2[mn]
输出
kkkmnmn

个人代码(有误)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
 string s;
cin>>s;
stack<string> wu;
// string t="";
int index=0;
for(int i=0;i<s.size();i++)
{
//cout<<s[i-1]<<endl;
char t=s[i];
if(t=='[')//左边可能是数字
{
wu.push(s.substr(index,i-index));
index=i+1;
// cout<<t;
// t="";
}
if(t==']')//字母
{
string t=s.substr(index,i-index);
string qianzhui=wu.top();//比如k2
cout<<qianzhui<<endl;
wu.pop();//弹出k2
string num="",zimu="";
//找出重复次数
for(int i=0;i<qianzhui.size();i++)
{
if(isdigit(qianzhui[i]))
num+=qianzhui[i];
else zimu+=qianzhui[i];
}

//if(zimu.size()!=0) wu.push(zimu);
// cout<<zimu<<endl;
if(j.size()!=0)
{
int j=stoi(num);

cout<<j;

string result="";
while(j>0)
{
result+=t;
//t+=t; 不可取
j--;
}
// return 0;
// cout<<t<<endl;
if(zimu.size()!=0) result=zimu+result;
wu.push(result);
}


index=i+1;
}
// else if(s[i]==']')
// {
// wu.pop();//此时还是[,字母还没放进去,t就是字母
// int num=stoi(wu.top());
// while(num>1)//重复num次
// {
// t+=t;
// num--;
// }
// wu.pop();//弹出数字
// cout<<t<<endl;
// wu.push(t);
// t="";
// }
}
while(!wu.empty())
{
cout<<wu.top()<<endl;
wu.pop();
}
return 0;
}

3[km23[a]]
km23
23aaaaaaaaaaaaaaaaaaaaaaa
terminate called after throwing an instance of ‘std::invalid_argument’
what(): stoi

3[kmaaa..aa]又遇到了],所以wu.top()即kmaaa..a当做了数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
int main()
{

string s;
cin>>s;
stack<string> cishu;
stack<string> zifu;//记录[]以外的值
// string t="";
int index=0;
for(int i=0;i<s.size();i++)
{
//cout<<s[i-1]<<endl;
char t=s[i];
if(t=='[')//左边可能是数字
{
cishu.push(s.substr(index,i-index));

index=i+1;
// cout<<t;
// t="";
}
if(t==']')//字母
{

cout<<index<<","<<i-index<<endl;
// if(i-index!=0)//不是最后一个]
string t=s.substr(index,i-index);
if(i-index==0)
{
t=zifu.top();
zifu.pop();
}


// cout<<t<<endl;

string qianzhui=cishu.top();//比如k2
// cout<<qianzhui<<endl;
cishu.pop();//弹出k2
string num="",zimu="";
//找出重复次数
for(int i=0;i<qianzhui.size();i++)
{
if(isdigit(qianzhui[i]))
num+=qianzhui[i];
else zimu+=qianzhui[i];
}

//if(zimu.size()!=0) wu.push(zimu);
// cout<<zimu<<endl;
int j=stoi(num);
// cout<<j<<endl;
// cout<<j;

string result="";
while(j>0)
{
result+=t;
//t+=t; 不可取
j--;
}
// return 0;
// cout<<t<<endl;
if(zimu.size()!=0) result=zimu+result;
zifu.push(result);

index=i+1;
}

}
while(!zifu.empty())
{
cout<<zifu.top()<<endl;
zifu.pop();
}
while(!cishu.empty())
{
cout<<cishu.top()<<endl;
cishu.pop();
}
return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
 string s;
cin>>s;
stack<string> cishu;//记录[左边的值
stack<string> zifu;//记录[]中间的值
// string t="";
int index=0;
for(int i=0;i<s.size();i++)
{
//cout<<s[i-1]<<endl;
char t=s[i];
if(t=='[')//左边可能是数字
{
cishu.push(s.substr(index,i-index));

index=i+1;
// cout<<t;
// t="";
}
if(t==']')//字母
{

// cout<<index<<","<<i-index<<endl;
// if(i-index!=0)//不是最后一个]
string t=s.substr(index,i-index);//bug利用1
// if(i-index==0)
// {
// t=zifu.top();
// zifu.pop();
// }


// cout<<t<<endl;

string qianzhui=cishu.top();//比如k2
// cout<<qianzhui<<endl;
if(cishu.size()>1)
cishu.pop();// bug利用2
string num="",zimu="";
//找出重复次数
for(int i=0;i<qianzhui.size();i++)
{
if(isdigit(qianzhui[i]))
num+=qianzhui[i];
else zimu+=qianzhui[i];
}

//if(zimu.size()!=0) wu.push(zimu);
// cout<<zimu<<endl;
int j=stoi(num);
// cout<<j<<endl;
// cout<<j;

string result="";
while(j>0)
{
result+=t;
//t+=t; 不可取
j--;
}
// return 0;
// cout<<t<<endl;
if(zimu.size()!=0) result=zimu+result;
zifu.push(result);

index=i+1;
}

}
/*有一个bug的利用就是:
如果没有嵌套的[],如3[k]2[mn],zifu栈不会有多余的值
如果有嵌套的[],如3[k2[mn]],zifu栈会有个多余值空值
因为string t=s.substr(8,0);执行了这个,赋值了空值
*/

s="";
int flag=-1;
while(!zifu.empty())
{
if(zifu.top()=="") flag=1;//一旦有嵌套
s=zifu.top()+s;
zifu.pop();
}
if(flag>0)
{
flag=stoi(cishu.top());
while(flag>0)
{
cout<<s;
flag--;
}
}
else cout<<s;


return 0;
}

3[k2[m]3[i]2[i2[p]]]
kmmiiiippkmmiiiippkmmiiiipp

2[k2[m]3[w2[p]]]
kmmwppkmmwpp

多于两层嵌套就有问题

个人代码2(处理不了非嵌套括号)

正则匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <string>
#include <regex>
using namespace std;

regex PATTERN("\\d+\\[[a-z]+]");
// \d匹配数字,+一到无穷个前方元素
// [ [a-z]+ ] 外括号里的内容,匹配a-z
string solution(const string& line) {
smatch match;
if (regex_search(line, match, PATTERN)) {
for(auto a:match)
cout<<a<<" ";

string group = match[0];
int pos = group.find('[');
//重复次数
int times = stoi(group.substr(0, pos));

int endpos=group.find(']');
//待重复字母
string words = group.substr(pos + 1, endpos-pos-1);
// cout<<words<<endl;
//记录重复字符
string builder;
for (int i = 0; i < times; i++) {
builder += words;
}

//3[m2[k]]
// string fixed = line.replace(,group.size(),builder);
string fixed=regex_replace(line,PATTERN,builder);
//3[mkk]
// cout<<fixed<<endl;
if(fixed==line)
return fixed;
}
return solution(fixed);
}

int main() {
string line;
// = "3[k]2[mn]"
cin>>line;
cout<<solution(line) ;

return 0;
}

这个可以处理非嵌套括号,emmmm

1
2
3
4
5
6
7
8
9
10
11
12
void solution( string& line) {
smatch match;
// if (regex_search(line, match, PATTERN)) { //它只查找到第一个匹配项就停止
// 3[k]2[mn] 找到3[k]就完了
sregex_iterator rit(line.begin(),line.end(),PATTERN);
sregex_iterator rend;
while(rit!=rend)
{
cout<<rit->str()<<endl;
rit++;
}
}

最终代码

问了chatgpt,它说用递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
string solution(const string& s, int &i) {
string result;
int count=0;
while(i<s.size()){
char c=s[i++];

if(isdigit(c))
{
count=count*10+(c-'0');//妙哇
}
else if(isalpha(c))
{
result+=c;
}
else if(c=='[')
{
string decode=solution(s,i);
for(int j=0;j<count;j++)
{
result+=decode;
}
count=0;
}
else if(c==']')
{
return result;
}
}
return result;
}

int main() {
string line;
// = "3[k]2[mn]"
//3[k2[mn]]
cin>>line;
int index=0;
cout<<solution(line,index);//不能直接赋值0

return 0;
}

cout一下过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
string solution(const string& s, int &i) {
string result;
int count=0;
while(i<s.size()){
char c=s[i++];
cout<<"第"<<i<<"个字符"<<endl;
if(isdigit(c))
{
count=count*10+(c-'0');
cout<<"遇到了字符:"<<c<<"变成数字 "<<count<<endl;
}
else if(isalpha(c))
{
result+=c;
cout<<"遇到了字符:"<<c<<"加和字母 "<<result<<endl;
}
else if(c=='[')
{
cout<<"遇到了[ : "<<endl;
cout<<"递归调用solution(s,i)"<<" 此时i为 "<<i<<endl;
string decode=solution(s,i);
cout<<"调用后的 decode 为:"<<decode<<endl;

cout<<"经过count "<<count<<"和result "<<result;
for(int j=0;j<count;j++)
{
result+=decode;
}
cout<<"处理后的result为 "<<result<<endl;
count=0;
cout<<"count归零"<<endl;
}
else if(c==']')
{
cout<<"遇到了] :"<<endl;
cout<<"return result "<<result<<endl;
return result;
}
}
cout<<"循环完毕,return result "<<result<<endl;
return result;
}

3[k2[mn]]
第1个字符
遇到了字符:3变成数字 3
第2个字符
遇到了[ :
递归调用solution(s,i) 此时i为 2
第3个字符
遇到了字符:k加和字母 k
第4个字符
遇到了字符:2变成数字 2
第5个字符
遇到了[ :
递归调用solution(s,i) 此时i为 5
第6个字符
遇到了字符:m加和字母 m
第7个字符
遇到了字符:n加和字母 mn
第8个字符
遇到了] :
return result mn
调用后的 decode 为:mn
经过count 2和result k处理后的result为 kmnmn
count归零
第9个字符
遇到了] :
return result kmnmn
调用后的 decode 为:kmnmn
经过count 3和result 处理后的result为 kmnmnkmnmnkmnmn
count归零
循环完毕,return result kmnmnkmnmnkmnmn
kmnmnkmnmnkmnmn

3[k]2[mn]
第1个字符
遇到了字符:3变成数字 3
第2个字符
遇到了[ :
递归调用solution(s,i) 此时i为 2
第3个字符
遇到了字符:k加和字母 k
第4个字符
遇到了] :
return result k
调用后的 decode 为:k
经过count 3和result 处理后的result为 kkk
count归零
第5个字符
遇到了字符:2变成数字 2
第6个字符
遇到了[ :
递归调用solution(s,i) 此时i为 6
第7个字符
遇到了字符:m加和字母 m
第8个字符
遇到了字符:n加和字母 mn
第9个字符
遇到了] :
return result mn
调用后的 decode 为:mn
经过count 2和result kkk处理后的result为 kkkmnmn
count归零
循环完毕,return result kkkmnmn
kkkmnmn

注意的点:

1.参数为int &n,实参不能直接给数字,需要通过一个变量声明

2.const int &n,虽然可以解决上述问题,但是,如果只能对n只读访问了,不可修改

40.最长的元音字符串

题目描述

定义当一个字符串只有元音字母(a,e,i,o,u,A,E,I,O,U)组成,
称为元音字符串,现给定一个字符串,请找出其中最长的元音字符串,
并返回其长度,如果找不到请返回0
字符串中任意一个连续字符组成的子序列称为该字符串的子串

输入描述

一个字符串其长度 0 < length ,字符串仅由字符a-zA-Z组成

输出描述

一个整数,表示最长的元音字符子串的长度

示例一

输入

1
asdbuiodevauufgh

输出

1
3

个人代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int main()
{
vector<char> yuanyin={'a','e','i','o','u','A','E','I','O','U'};
string s;
cin>>s;

int len=0,co=0,flag=-1;
for(int i=0;i<s.size();i++)
{

char cur=s[i];
for(auto a:yuanyin)
{
if(cur==a)
{
flag=1;
break;
}
}
if(flag>0)
{
co++;
len=max(len,co);
flag=-9;
}
else co=0;
}
cout<<len;

return 0;
}

41.*删除目录

题目描述

某文件系统中有N个目录,
每个目录都一个独一无二的ID。
每个目录只有一个父目录,
但每个目录下可以有零个或多个子目录,
目录结构呈树状结构。
假设 根目录的ID为0,且根目录没有父目录
ID用唯一的正整数表示,并统一编号
现给定目录ID和其付目录ID的对应父子关系表
[子目录ID,父目录ID],以及一个待删除的目录ID,
请计算并返回一个ID序列,
表示因为删除指定目录后剩下的所有目录,
返回的ID序列以递增序输出
注意:
1、被删除的目录或文件编号一定在输入的ID序列中;
2、当一个目录删除时,它所有的子目录都会被删除。

输入描述

输入的第一行为父子关系表的长度m;接下来的m行为m个父子关系对;
最后一行为待删除的ID。
序列中的元素以空格分割,
参见样例。

输出描述

输出一个序列,表示因为删除指定目录后,剩余的目录ID。

示例一

输入

1
2
3
4
5
6
7
5
8 6
10 8
6 0
20 8
2 6
8

输出

1
2 6

个人代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
set<int> list;
void zimulu(int a,map<int,int> s)
{

for(auto sm:s)
{
//
//
if(sm.second!=a&&sm.second!=0&&sm.first==a) list.insert(sm.second);
if(sm.second!=a&&sm.first!=a) list.insert(sm.first);

}

}
int main()
{
//[子,父]
int n;
cin>>n;
map<int,int> mp;
for(int i=0;i<n;i++)
{
int a,b;
cin>>a>>b;
mp[a]=b;
}
int m;//待删除目录ID
cin>>m;
zimulu(m,mp);

for(auto w:list)
{
cout<<w<<" ";

}

//cout<<w.first<<","<<w.second<<endl;
}

42.吃火锅

题目描述

入职后,导师会请你吃饭,你选择了火锅,
火锅里会在不同时间下很多菜,
不同食材要煮不同时间,才能变得刚好合适,
你希望吃到最多的刚好合适的菜,
但是你的手速不够快用m代替手速,
每次下手捞菜后至少要过m秒,
才能再捞(每次只能捞一个)那么用最合理的策略,
最多能吃到多少,刚好合适的菜

输入描述

第一行两个整数nm
其中n代表往锅里下菜的个数
m代表手速
接下来有n行,
每行有两个数xy
代表第x秒下的菜过y秒才能变得刚好合适(1 < mn < 1000),(1 < xy < 1000

输出描述

输出一个整数代表用最合理的策略,最多能吃到刚好合适的菜的数量

示例一

输入

1
2
3
2 1
1 2
2 1

输出

1
1

个人代码(待测试)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int main()
{
int n,m;//菜的个数,手速
cin>>n>>m;
vector<pair<int,int> >a(n);
for(int i=0;i<n;i++)
{
int b,c;
cin>>b>>c;
a[i]=make_pair(b,c);//第bs的菜要cs才能吃
}
//那就是看间隔咯? b+c与之前的相减,>=m那就是可以咯
int pre=a[0].first+a[0].second;

int co=0;
for(auto list:a)
{
int cur=list.second+list.first;
if(cur-pre>=m) co++;
pre=cur;
}

cout<<co+1;

}

65.服务器广播

题目描述

服务器连接方式包括直接相连,间接连接。
AB直接连接,BC直接连接,则AC间接连接。
直接连接和间接连接都可以发送广播。
给出一个N*N数组,代表N个服务器,matrix[i][j] == 1
则代表ij直接连接;不等于1时,代表ij不直接连接。
matrix[i][i] == 1
即自己和自己直接连接。matrix[i][j] == matrix[j][i]
计算初始需要给几台服务器广播,
才可以使每个服务器都收到广播。

输入描述

输入为N行,每行有N个数字,为01,由空格分隔,
构成N*N的数组,N的范围为 1 <= N <= 40

输出描述

输出一个数字,为需要广播的服务器的数量

示例一

输入

1
2
3
1 0 0
0 1 0
0 0 1

Copy

输出

1
3

代码(有误)

本质是映射map<int,vector<int>> 0,1,2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include<iostream>
#include<vector>
#include<map>
#include<sstream>
#include<algorithm>
using namespace std;
void solution(int **a,int n)
{
/*
0 1
1 0
*/
map<int,vector<int> >mp;
int mpkey=0;
for(int i=0;i<n;i++)
{
cout<<"now "<<i<<endl;
bool isexist=false;
for(auto &item:mp)//加个&,表示可修改
for(int &it:item.second)
{
if(it==i)
{
cout<<it<<endl;
isexist=true;
mpkey=item.first;
}
}
if(!isexist)//不包含,新开一项
{
// vector<int> list;
mp[i]= vector<int>();
mpkey=i;
cout<<"now key"<<mpkey<<endl;
}
for(int k=i;k<n;k++)
{
// vector<int> tem;
if(i!=k && a[i][k]==1)
{
mp[mpkey].push_back(k);
// tem.push_back(k);
}

}
}
cout<<mp.size();
}
int main()
{
string s,t;
vector<int> a;
getline(cin,s);
cout<<s.size()<<endl;
for(int i=0;i<s.size();i++)
{
if(s[i]!=' ') a.push_back(s[i]-'0');
}
int n=a.size();//n维
cout<<a.size()<<endl;

//int an[n][n];
int **an=new int*[n];

for(int i=0;i<n;i++)// 第一行
an[0][i]=a[i];
for(int i=1;i<n;i++)
{
string t;
getline(cin,s);
stringstream ss(s);
int co=0;
while(getline(ss,t,' '))
{
an[i][co]=t[0]-'0';
co++;
if(co==n) break;
}
}
solution(an,n);
// for(int i=0;i<n;i++)
// {
// for(int j=0;j<n;j++)
// cout<<an[i][j]<<",";

// cout<<endl;
// }

/*
a[i][j]即a[j][i]为1表示直接相连 a[i][i]自己相连

既然是广播,那就是第一行与每一列
*/

}

66.二叉树广度优先遍历

即层序遍历。

从上到下,从左到右依次打印出来,就是ABCDEFG。

实现思路,用队列实现。A入队,从根结点开始。接着A的两个孩子不空,BC入队(A出队,队头指向B);然后B的两个孩子DE入队,B出队,队头指向C;C的两个孩子FG进入,C出队;最后将DEFG输出。

深度优先遍历就是,前中后序遍历

67.找单词

题目描述

给一个字符串和一个二维字符数组,如果该字符串存在于该数组中,则按字符串的字符顺序输出字符串每个字符所在单元格的位置下标字符串,如果找不到返回字符串N

  1. 需要按照字符串的字符组成顺序搜索,且搜索到的位置必须是相邻单元格,其中“相邻单元格”是指那些水平相邻或垂直相邻的单元格。
  2. 同一个单元格内的字母不允许被重复使用。
  3. 假定在数组中最多只存在一个可能的匹配。

输入描述

  1. 第1行为一个数字N指示二维数组在后续输入所占的行数。
  2. 第2行到第N+1行输入为一个二维大写字符数组,每行字符用半角,分割。
  3. N+2行为待查找的字符串,由大写字符组成。
  4. 二维数组的大小为N*N0 < N <= 100
  5. 单词长度K0 < K < 1000

输出描述

输出一个位置下标字符串,拼接格式为:第1个字符行下标+”,”+第1个字符列下标+”,”+第2个字符行下标+”,”+第2个字符列下标…+”,”+第N个字符行下标+”,”+第N个字符列下标示例1

示例一

输入

1
2
3
4
5
6
4
A,C,C,F
C,D,E,D
B,E,S,S
F,E,C,A
ACCESS

输出

1
0,0,0,1,0,2,1,2,2,2,2,3

代码1(不符题意)

哦,这个顺序搜索有说法的

**A,C,C,**F
C,D,**E,**D
B,E,S,S
F,E,C,A
ACCESS

反正最终路径有横有折,一点点斜的都不要

这玩意儿想到了什么,走迷宫哇。只能横向,竖向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
int main()
{
int n;
cin>>n;
cin.ignore();
string s,t;
vector<vector<string> >a(n,vector<string>(n,""));
for(int i=0;i<n;i++)
{
cin>>s;
stringstream ss(s);
int j=0;
// vector<string> aa;
while(getline(ss,t,','))
{
//aa.push_back(t);
if(j==n) break;//退出循环
a[i][j]=t;
// cout<<j<<endl;
j++;

}
// a.push_back(aa);
}

// for(int i=0;i<n;i++)
// {
// for(int j=0;j<n;j++)
// {
// cout<<a[i][j];
// }
// cout<<endl;
// }

string tt;
cin>>tt;

int x=0,y=0,i=0,len=tt.size();
string tem="",result="";
while(i<len)
{
tem=tt[i];
// cout<<a[x][y]<<endl;
if(a[x][y]==tem)
{
i++;
// cout<<x<<","<<y;
// if(i!=len-1) cout<<",";
result+=to_string(x)+","+to_string(y)+",";
tem="";
}
y++;//列坐标移动
if(y==n)
{
y=0;
x++;
}
// if(x==n) break;

}
// cout<<a[0][1];
cout<<result.substr(0,result.size()-1);



}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
vector<int> res;
int check(vector<vector<string> > a,int r,int c,string s,int i)
{
// int j=a.size();
string next="";
next=s[i];
if(r+1<a.size()&&c+1<a.size())
{
if(a[r+1][c]==next) return 1;
else if(a[r][c+1]==next) return 2;
}
return 0;
}
void search(vector<vector<string> > a,string s,int row,int col,int i)
{
if(i>s.size())
{
return;
}

string cur="";
cur+=s[i];

if(i==0)
{
for(int i=0;i<a.size();i++)
for(int j=0;j<a.size();j++)
{
if(a[i][j]==cur)
{
row=i;
col=j;
cout<<row<<","<<col<<endl;
break;
}
}
}

res.push_back(row);
res.push_back(col);

if(check(a,row,col,s,i+1)==1)//往下走
{
search(a,s,row+1,col,i+1);
}
else if(check(a,row,col,s,i+1)==2)//往右走
{
search(a,s,row,col+1,i+1);
}



//N情况没有考虑
//有多个首值,第一个首值不一定成功,没有考虑
//行和列值相同时,先取哪一个没有考虑
}
int main()
{
int n;
cin>>n;
cin.ignore();
string s,t;
vector<vector<string> >a(n,vector<string>(n,""));
//vector<string> a(n);
for(int i=0;i<n;i++)
{
cin>>s;
stringstream ss(s);
int j=0;
// string tt="";

while(getline(ss,t,','))
{
a[i][j]=t;
j++;
if(j>n) break;
// tt+=t;
}
// a.push_back(tt);
}

// if(a[0][0]=='A') cout<<"erer"<<endl;
if(a[0][0]=="A") cout<<"~~~"<<endl;


string tt;
cin>>tt;

int x=0,y=0,i=0,len=tt.size();
string tem="",result="";

search(a,tt,0,0,0);
// cout<<a[0][1];
for(int li:res)
cout<<li<<",";



}

二维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  char a[n][n];
//vector<string> a(n);
for(int i=0;i<n;i++)
{
cin>>s;
int j=0;
for(int k=0;k<s.size();k++)
{
if(s[k]!=',')
{
a[i][j]=s[k];
j++;
}
}
// a.push_back(tt);
}
但是作为函数调用的参数,报错的比较多,还是用vector吧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
4
A,C,C,F
C,D,E,D
B,E,S,S
F,E,C,A
ACCESS
move to 0,0
now s[i] is A i: 0
move to 1,0
now s[i] is C i: 1
move to 0,1
now s[i] is C i: 1
move to 0,2
now s[i] is C i: 2
move to 1,2
now s[i] is E i: 3
move to 2,2
now s[i] is S i: 4
move to 2,3
now s[i] is S i: 5
move to 3,3
now s[i] is A i: 0


是什么导致这个i,往回去了嘞,我把主函数的双for循环去掉了,没必要,直接从0开始即可

分析阶段

代码片段1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
bool check(vector<vector<char> >a,int row,int col,string s,int i,vector<vector<int> >visit,vector<int> &res)
{

if(a[row][col]!=s[i]) return false;
// else if(i==s.size()-1) return true;这样会导致最后一个不执行的哇
// else if(i==s.size()-1) return true;//把s的字符都找到了
if(i==s.size())
{
cout<<"end now"<<endl;
return true;
}

cout<<"move to "<<row<<","<<col<<endl;
cout<<"now s[i] is "<<s[i]<<" i: "<<i<<endl;

res.push_back(row);
res.push_back(col);
cout<<"insert "<<row<<","<<col<<endl;
visit[row][col]=1;
// if(row-1>=0&&a[row-1][col]==s[i+1]&&visit[row-1][col]==0)//上

if(row-1>=0&&visit[row-1][col]==0)//上
{
check(a,row-1,col,s,i+1,visit,res);

// return true;
}

if(row<=a.size()-1&&visit[row+1][col]==0)//下
{
check(a,row+1,col,s,i+1,visit,res);

// return true;
}

if(col-1>=0&&visit[row][col-1]==0)//左
{
check(a,row,col-1,s,i+1,visit,res);

// return true;
}

if(col<=a.size()-1&&visit[row][col+1]==0)//右
{
check(a,row,col+1,s,i+1,visit,res);

// return true;
}
/*认为如果res长度够,那就不用回退,
那塔喵取决于字符串长度了*/
// if(res.size()!=s.size()*2)
// {
res.pop_back();
res.pop_back();
cout<<"delete"<<row<<","<<col<<endl;
// }


//递归完执行到这里说明走不完
visit[row][col]=0;
// return result;
return false;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
move to 0,0
now s[i] is A i: 0
move to 1,0
now s[i] is C i: 1
move to 0,1
now s[i] is C i: 1
move to 0,2
now s[i] is C i: 2
move to 1,2
now s[i] is E i: 3
move to 2,2
now s[i] is S i: 4
move to 2,3
now s[i] is S i: 5
0

现在的问题是,全局变量res,加不进去坐标嘞

详细点
move to 0,0
now s[i] is A i: 0
insert 0,0
move to 1,0
now s[i] is C i: 1
insert 1,0
delete1,0
move to 0,1
now s[i] is C i: 1
insert 0,1
move to 0,2
now s[i] is C i: 2
insert 0,2
move to 1,2
now s[i] is E i: 3
insert 1,2
move to 2,2
now s[i] is S i: 4
insert 2,2
move to 2,3
now s[i] is S i: 5
insert 2,3
delete2,3
delete2,2
delete1,2
delete0,2
delete0,1
delete0,0
0

它这相当于回退完了,按理说都执行i==s.size(),return true了,都终止条件了,为什么还回溯

怎么说呢,当前实例的问题,更深层次的递归达到条件了,它要释放,那么次深层的也要释放,直到第一次递归释放为止,所以要对每一次递归进行判断,而不是只对结果判断,塔喵的!!

问题解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
bool check(vector<vector<char> >a,int row,int col,string s,int i,vector<vector<int> >visit,vector<int> &res)
{

if(a[row][col]!=s[i]) return false;
// else if(i==s.size()-1) return true;这样会导致最后一个不执行的哇
// else if(i==s.size()-1) return true;//把s的字符都找到了
if(i==s.size())
{
cout<<"end now"<<endl;
return true;
}

cout<<"move to "<<row<<","<<col<<endl;
cout<<"now s[i] is "<<s[i]<<" i: "<<i<<endl;

res.push_back(row);
res.push_back(col);
cout<<"insert "<<row<<","<<col<<endl;
visit[row][col]=1;
// if(row-1>=0&&a[row-1][col]==s[i+1]&&visit[row-1][col]==0)//上

if(row-1>=0&&visit[row-1][col]==0)//上
{
if(check(a,row-1,col,s,i+1,visit,res))
return true;

// return true;
}

if(row<=a.size()-1&&visit[row+1][col]==0)//下
{
if(check(a,row+1,col,s,i+1,visit,res))
return true;

// return true;
}

if(col-1>=0&&visit[row][col-1]==0)//左
{
if(check(a,row,col-1,s,i+1,visit,res))
return true;

// return true;
}

if(col<=a.size()-1&&visit[row][col+1]==0)//右
{
if(check(a,row,col+1,s,i+1,visit,res))
return true;

// return true;
}
/*认为如果res长度够,那就不用回退,
那塔喵取决于字符串长度了*/
// if(res.size()!=s.size()*2)
// {
res.pop_back();
res.pop_back();
cout<<"delete"<<row<<","<<col<<endl;
// }


//递归完执行到这里说明走不完
visit[row][col]=0;
// return result;
return false;

}

错误代码1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include <iostream>
#include <vector>
#include <map>
#include <sstream>
using namespace std;

// vector<int> res1;
bool check(vector<vector<char>> a, int row, int col, string s, int i, vector<vector<int>> visit, vector<int> &res)
{

if (a[row][col] != s[i])
return false;
// else if(i==s.size()-1) return true;这样会导致最后一个不执行的哇
// else if(i==s.size()-1) return true;//把s的字符都找到了
if (i == s.size())
{
cout << "end now" << endl;
return true;
}

cout << "move to " << row << "," << col << endl;
cout << "now s[i] is " << s[i] << " i: " << i << endl;

res.push_back(row);
res.push_back(col);
cout << "insert " << row << "," << col << endl;
visit[row][col] = 1;
// if(row-1>=0&&a[row-1][col]==s[i+1]&&visit[row-1][col]==0)//上

if (row - 1 >= 0 && visit[row - 1][col] == 0) // 上
{
if (check(a, row - 1, col, s, i + 1, visit, res))
return true;

// return true;
}

if (row <= a.size() - 1 && visit[row + 1][col] == 0) // 下
{
if (check(a, row + 1, col, s, i + 1, visit, res))
return true;

// return true;
}

if (col - 1 >= 0 && visit[row][col - 1] == 0) // 左
{
if (check(a, row, col - 1, s, i + 1, visit, res))
return true;

// return true;
}

if (col <= a.size() - 1 && visit[row][col + 1] == 0) // 右
{
if (check(a, row, col + 1, s, i + 1, visit, res))
return true;

// return true;
}
/*认为如果res长度够,那就不用回退,
那塔喵取决于字符串长度了*/
// if(res.size()!=s.size()*2)
// {
res.pop_back();
res.pop_back();
cout << "delete" << row << "," << col << endl;
// }

// 递归完执行到这里说明走不完
visit[row][col] = 0;
// return result;
return false;
}

int main()
{
int n;
cin >> n;
cin.ignore();
string s, t;
vector<vector<char>> a(n, vector<char>(n));
// vector<string> a(n);
for (int i = 0; i < n; i++)
{
cin >> s;
int j = 0;
for (int k = 0; k < s.size(); k++)
{
if (s[k] != ',')
{
a[i][j] = s[k];
j++;
}
}
// a.push_back(tt);
}

string tt;
cin >> tt;

// search(a,tt,0,0,0);
// cout<<a[0][1];
int flag = 1;
vector<vector<int>> visit(n, vector<int>(n, 0));

int i = 0, j = 0;
vector<int> res; // 记录坐标
bool found = check(a, i, j, tt, 0, visit, res);
if (!found)
cout << "N";
else
{

for (int i = 0; i < res.size(); i++)
{
cout << res[i];
if (i != res.size() - 1)
cout << ",";
}
}
}

错误代码2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include <iostream>
#include <vector>
#include <map>
#include <sstream>
using namespace std;

vector<int> res; // 记录坐标
vector<int> path;
bool check(vector<vector<char>> a, int row, int col, string s, int i, vector<vector<int>> &visit)
{
if (a[row][col] != s[i])
return false;
// else if(i==s.size()-1) return true;这样会导致最后一个坐标不执行的哇
// else if(i==s.size()-1) return true;//把s的字符都找到了
else if (i == s.size()-1)//关于这个i==s.size()-1 和i==s.size()
{
return true;
}

cout << "move to " << row << "," << col << endl;
cout << "now s[i] is " << s[i] << " i: " << i << endl;

// res.push_back(row);
// res.push_back(col);
cout << "insert " << row << "," << col << endl;
visit[row][col] = 1;
bool result=false;
// if(row-1>=0&&a[row-1][col]==s[i+1]&&visit[row-1][col]==0)//上

if (row - 1 >= 0&&visit[row-1][col]==0) // 上
{
if(check(a, row - 1, col, s, i + 1, visit))
result=true;
}

if (row < a.size() &&visit[row+1][col]==0) // 下
{
if(check(a, row + 1, col, s, i + 1, visit))
result=true;
}

if (col - 1 >= 0 &&visit[row][col-1]==0) // 左
{
if(check(a, row, col - 1, s, i + 1, visit))
result=true;
}

if (col < a.size() &&visit[row][col+1]==0) // 右
{
if(check(a, row, col + 1, s, i + 1, visit))
result=true;
}

cout << "delete" << row << "," << col << endl;
// 递归完执行到这里说明走不完
visit[row][col] = 0;

// }
cout<<"do the successs"<<endl;

return result;
}
bool exist(vector<vector<char>> a,string s)
{

int n=a.size();
vector<vector<int>> visit(n, vector<int>(n, 0));
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
bool found = check(a, i, j, s, 0, visit);
if(found)
{
res.push_back(i);
res.push_back(j);
return found;
}
}

}
int main()
{
int n;
cin >> n;
cin.ignore();
string s, t;
vector<vector<char>> a(n, vector<char>(n));
// vector<string> a(n);

for (int i = 0; i < n; i++)
{
cin >> s;
int j = 0;
for (int k = 0; k < s.size(); k++)
{
if (s[k] != ',')
{
a[i][j] = s[k];
j++;
}
}
// a.push_back(tt);
}

string tt;
cin >> tt;

// search(a,tt,0,0,0);
// cout<<a[0][1];
int flag = 1;


//int i = 0, j = 0;
//bool found = check(a, i, j, tt, 0, visit);//2.只能保证开头0,0是入口的情况
//若在主程序中使用exsit函数的内容,它是不会适可而止的,即迷宫走完,它也会循环从入口继续走
//所以接收返回值
bool found=exist(a,tt);
for(int i=0;i<res.size();i++)
{
cout<<res[i];
if(i!=res.size()-1) cout<<",";
}

}

非递归尝试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
int n=a.size();
vector<vector<int>> visit(n,vector<int>(n,0));
int i=0;
while(i<s.size())
{
if(col>=a.size()||row>=a.size()||col<0||row<0)
{
break;
}
if(a[row][col]!=s[i])//找入口
{
col++;
if(col==a.size())
{
row++;
col=0;
}
}
else if(a[row][col]==s[i])//找到了
{
res.push_back(row);
res.push_back(col);

// i++;
visit[row][col]=1;

if(row-1>=0&&visit[row-1][col]==0)//上
{
row=row-1;
i++;
}
else if(row+1<=a.size()&&visit[row+1][col]==0)//下
{
row=row+1;
i++;
}
else if(col-1>=0&&visit[row][col-1]==0)//左
{
col=col-1;
i++;
}
else if(col+1<=a.size()&&visit[row][col+1]==0)//右
{
col=col+1;
i++;
}
else
{
res.pop_back();
res.pop_back();
i--;
visit[row][col]=0;
}
}
}

遇到的问题有:

1.还原的问题

2.然后是一条道走到底的问题

所以为什么递归很香。。。。

关键之处

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
4
A,C,C,F
C,D,E,D
B,E,S,S
F,E,C,A
ACCESS
input row 0,col 0
input row 1,col 0
no match~~~~
2,0 B, s[i]2 C
no match~~~~
1,1 D, s[i]2 C
delete row 1,col 0
input row 0,col 1
no match~~~~
1,1 D, s[i]2 C
input row 0,col 2
input row 1,col 2
input row 2,col 2
no match~~~~
3,2 C, s[i]5 S
no match~~~~
2,1 E, s[i]5 S
input row 2,col 3
end now!!!!
end now!!!! /////好,主要看下这后面是什么情况,他喵的都return true啦
delete row 2,col 3
delete row 2,col 2
no match~~~~
1,1 D, s[i]4 S
no match~~~~
1,3 D, s[i]4 S
delete row 1,col 2
no match~~~~
0,3 F, s[i]3 E
delete row 0,col 2
delete row 0,col 1
delete row 0,col 0
1,0,2,3,2,2,1,2,0,2,0,1,0,0

既然这样,我就在return true的时候,将那个res(终会pop完的),复制给一个新的vector,path;对吧,在res它pop完之前,完整交给另一个数组即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
bool check(vector<vector<char>> a, int row, int col, string s, int i,  vector<vector<int>>& visit)
{

int n=a.size();
if(col>=a.size()||row>=a.size()||col<0||row<0)
{
return false;
}

if(i==s.size())
{
cout<<"end now!!!!"<<endl;
path=res; ////我不管,点睛之笔
return true;
}

bool result=false;

if(a[row][col]!=s[i])//判断找到了没
{
cout<<"no match~~~~"<<endl;
cout<<row<<","<<col<<" "<<a[row][col]<<", s[i]"<<i<<" "<<s[i]<<endl;
// res.clear(); //我在这里清空
return false;
}
res.push_back(row);
res.push_back(col);
// else if(a[row][col]==s[i])//找到了
// {
// res.push_back(row);
// res.push_back(col);
cout<<"input row "<<row<<",col "<<col<<endl;
// i++;
visit[row][col]=1;

if(row-1>=0&&visit[row-1][col]==0)//上
{
// check(a,row-1,col,s,i+1,visit);

if(check(a,row-1,col,s,i+1,visit))
result=true;
}
// else if(row+1<=a.size()&&visit[row+1][col]==0)//下
if(row+1<=a.size()&&visit[row+1][col]==0)//下
{
// row=row+1;
// i++;
// check(a,row+1,col,s,i+1,visit);
if(check(a,row+1,col,s,i+1,visit))
result=true;
}
//else if(col-1>=0&&visit[row][col-1]==0)//左
if(col-1>=0&&visit[row][col-1]==0)//左
{
// check(a,row,col-1,s,i+1,visit);
if(check(a,row,col-1,s,i+1,visit))
result=true;
}
// else if(col+1<=a.size()&&visit[row][col+1]==0)//右
if(col+1<=a.size()&&visit[row][col+1]==0)//右
{
// check(a,row,col+1,s,i+1,visit);
if(check(a,row,col+1,s,i+1,visit))
result=true;
}
// else
// {

// }
cout<<"delete row "<<row<<",col "<<col<<endl;
res.pop_back();
res.pop_back();

visit[row][col]=0;

return result;
// }

}

好的,新问题出现,这只适用于入口在0,0的地方,如果是其它地方嘞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
4
A,C,C,F
C,D,E,D
B,E,S,S
F,E,C,A
FDSA
no match~~~~
0,0 A, s[i]0 F
no match~~~~
0,1 C, s[i]0 F
no match~~~~
0,2 C, s[i]0 F
input row 0,col 3
input row 1,col 3
input row 2,col 3
input row 3,col 3
//哦豁?????????????
//1.都没有写进去咩?
//2.不回溯啦?


很好,试探了一下,if(i==s.size()-1)才会执行,塔喵的,那不就少一个坐标了
所以是什么导致i不加了,使得不能=s.size()了


1
2
3
4
5
6
7
8
9
问题在这里 if(col+1<=a.size()&&visit[row][col+1]==0)//右

如果a是4行4列
现在已经是a[3][3]的位置了,那就是最右下角了
这个时候因为3+1==4,是满足滴,所以可以继续执行函数,但是嘞a[3][4]是溢出了滴,懂了


这样改
if(col+1<a.size()&&visit[row][col+1]==0)//右
1
2
3
4
5
6
7
8
int n=a.size();
vector<vector<int>> visit(n, vector<int>(n, 0));
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)//....这没用咩?
{
bool found = check(a, i, j, s, 0, visit);
return found;
}

最终代码(啧,不容易)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include <iostream>
#include <vector>
#include <map>
#include <sstream>
using namespace std;

vector<int> res; // 记录坐标
vector<int> path;
bool check(vector<vector<char>> a, int row, int col, string s, int i, vector<vector<int>> &visit)
{

int n = a.size();
if (col >= a.size() || row >= a.size() || col < 0 || row < 0)
{
return false;
}

if (i == s.size())
{
cout << "end now!!!!" << endl;
path = res;
return true;
}

bool result = false;

if (a[row][col] != s[i]) // 判断找到了没
{
cout << "no match~~~~" << endl;
cout << row << "," << col << " " << a[row][col] << ", s[i]" << i << " " << s[i] << endl;
// res.clear(); //我在这里清空
return false;
}
res.push_back(row);
res.push_back(col);
// else if(a[row][col]==s[i])//找到了
// {
// res.push_back(row);
// res.push_back(col);
cout << "input row " << row << ",col " << col << endl;
// i++;
visit[row][col] = 1;

if (row - 1 >= 0 && visit[row - 1][col] == 0) // 上
{
// check(a,row-1,col,s,i+1,visit);

if (check(a, row - 1, col, s, i + 1, visit))
result = true;
}
// else if(row+1<=a.size()&&visit[row+1][col]==0)//下
if (row + 1 < a.size() && visit[row + 1][col] == 0) // 下
{
// row=row+1;
// i++;
// check(a,row+1,col,s,i+1,visit);
if (check(a, row + 1, col, s, i + 1, visit))
result = true;
}
// else if(col-1>=0&&visit[row][col-1]==0)//左
if (col - 1 >= 0 && visit[row][col - 1] == 0) // 左
{
// check(a,row,col-1,s,i+1,visit);
if (check(a, row, col - 1, s, i + 1, visit))
result = true;
}
// else if(col+1<=a.size()&&visit[row][col+1]==0)//右
if (col + 1 < a.size() && visit[row][col + 1] == 0) // 右
{
// check(a,row,col+1,s,i+1,visit);
if (check(a, row, col + 1, s, i + 1, visit))
result = true;
}

cout << "delete row " << row << ",col " << col << endl;
res.pop_back();
res.pop_back();

// 那我不pop了好吧,我直接添加,大不了倒叙排列
// 但是这等于添加了所有路径,怎么筛选嘞
// 我武断地讲,找个时机,遇到false了,清除一下res,
// 要么把所有路径存起来咯
visit[row][col] = 0;

return result;
// }
}
bool exist(vector<vector<char>> a, string s)
{

int n = a.size();
vector<vector<int>> visit(n, vector<int>(n, 0));
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++) // 在我看来,这是为了确定入口的位置
{
bool found = check(a, i, j, s, 0, visit);
if (found) //必要性,是true了,才要对吧,不然就算了
return found;
}
return false;
}
int main()
{
int n;
cin >> n;
// cin.ignore();
string s, t;
vector<vector<char>> a(n, vector<char>(n));
// vector<string> a(n);

for (int i = 0; i < n; i++)
{
cin >> s;
int j = 0;
for (int k = 0; k < s.size(); k++)
{
if (s[k] != ',')
{
a[i][j] = s[k];
j++;
}
}
// a.push_back(tt);
}

string tt;
cin >> tt;

// search(a,tt,0,0,0);
// cout<<a[0][1];
int flag = 1;

// int i = 0, j = 0;
// bool found = check(a, i, j, tt, 0, visit);//2.只能保证开头0,0是入口的情况

vector<vector<int>> visit(n, vector<int>(n, 0));
bool found = exist(a, tt);
if(found)
{
for (int i = 0; i < path.size(); i++)
{
cout << path[i];
if (i != path.size() - 1)
cout << ",";
}
}
else cout<<"N";
cout << endl
<< res.size() << endl;
}

哎,不容易。

68.招聘

重叠区间问题

题目描述

某公司组织一场公开招聘活动,假设由于人数和场地的限制,
每人每次面试的时长不等,并已经安排给定,
(S1,E1)(S2,E2)(Sj,Ej)…(Si < Ei,均为非负整数)表示每场面试的开始和结束时间。
面试采用一对一的方式,即一名面试官同时只能面试一名应试者,
一名面试官完成一次面试后可以立即进行下一场面试,且每个面试官的面试人次不超过m
为了支撑招聘活动高效顺利进行,请你计算至少需要多少名面试官。

输入描述

输入的第一行为面试官的最多面试人次m,第二行为当天总的面试场次n,
接下来的n行为每场面试的起始时间和结束时间,起始时间和结束时间用空格分隔。
其中,1 <= n, m <= 500

输出描述

输出一个整数,表示至少需要的面试官数量。

示例一

输入

1
2
3
4
5
6
7
2
5
1 2
2 3
3 4
4 5
5 6

输出

1
3

说明

总共有5场面试
且面试时间都不重叠
但每个面试官最多只能面试2人次
所以需要3名面试官

示例二

输入

1
2
3
4
5
3
3
1 2
2 3
3 4

输出

1
1

说明

总共有3场面时,面试时间都不重叠
每个面试官最多能面试3人次
所以只需要一名面试官

示例三

输入

1
2
3
4
5
3
3
8 35
5 10
1 3

输出

1
2

个人代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct mianshi{
int l,r;
bool operator()(mianshi &a,mianshi &b)
{

return a.l<b.l||(a.l==b.l&&a.r<b.r);
}
};
int chongdie(vector<mianshi> a)
{
int co=0;
mianshi pre;
pre.l=a[0].l;
pre.r=a[0].r;
for(int i=1;i<a.size();i++)
{
if(pre.r>a[i].l&&pre.l<a[i].r)
co++;
//1.一开始少了这一步
else
{
pre.l=a[i].l;
pre.r=a[i].r;
}
}
return co;
}
int main()
{
int m,n;//一人最多面试m,总共面试n次
//没有重叠部分,n/m次,除不尽就加1
//有重叠部分,(n/m)+1
cin>>m>>n;
vector<mianshi> list;
for(int i=0;i<n;i++)
{
int a,b;
cin>>a>>b;
// a[i].push_back({a,b});
mianshi ms;
ms.l=a;
ms.r=b;
list.push_back(ms);
}

sort(list.begin(),list.end(),mianshi());
cout<<list.size()<<endl;
int co=chongdie(list);
cout<<co<<endl;
for(auto al:list)
{
cout<<al.l<<","<<al.r<<endl;
}


int num=(n%m==0?n/m:(n/m)+1);
if(co!=0)
{

cout<<num+co;
}
else cout<<num;


}

69.斗地主顺子

题目描述

在斗地主扑克牌游戏中,扑克牌由小到大的顺序为3 4 5 6 7 8 9 10 J Q K A 2
玩家可以出的扑克牌阵型有,单张对子顺子飞机炸弹
其中顺子的出牌规则为,由至少5张由小到大连续递增的扑克牌组成
且不能包含2
例如:{3,4,5,6,7}{3,4,5,6,7,8,9,10,J,Q,K,A}都是有效的顺子
{J,Q,K,A,2}{2,3,4,5,6}{3,4,5,6}{3,4,5,6,8}等都不是顺子
给定一个包含13张牌的数组,如果有满足出牌规则的顺子,请输出顺子
如果存在多个顺子,请每行输出一个顺子
且需要按照顺子的第一张牌的大小(必须从小到大)依次输出
如果没有满足出牌规则的顺子,请输出No

输入描述

13张任意顺序的扑克牌,每张扑克牌数字用空格隔开,
每张扑克牌的数字都是合法的
并且不包括大小王:2 9 J 2 3 4 K A 7 9 A 5 6
不需要考虑输入为异常字符的情况

输出描述

组成的顺子 每张扑克牌数字用空格隔开
3 4 5 6 7

示例一

输入

1
2 9 J 2 3 4 K A 7 9 A 5 6

输出

1
3 4 5 6 7

代码(错误)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
void zhuanhuan1(vector<string> &a)
{
for(int i=0;i<a.size();i++)
{
if(a[i]=="J") a[i]="11";
else if(a[i]=="Q") a[i]="12";
else if(a[i]=="K") a[i]="13";
else if(a[i]=="A") a[i]="14";
}
}
void zhuanhuan2(vector<string> &a)
{
for(int i=0;i<a.size();i++)
{
if(a[i]=="11") a[i]="J";
else if(a[i]=="12") a[i]="Q";
else if(a[i]=="13") a[i]="K";
else if(a[i]=="14") a[i]="A";
}
}
bool cmp(string &a,string &b)
{
int c1=stoi(a);
int c2=stoi(b);
return c1<c2;

}
vector<string> res;
vector<string> path;
void shunzi(vector<string> a)
{

int diff=0;
int i=0,j=1;
while(i<13)
{
//int pre=;
while(stoi(a[i])==2) i++;
int pre=stoi(a[i]);

while(stoi(a[j]) == 2) j++;
int cur = stoi(a[j]);

cout<<"now pre= "<<pre<<"noew cur= "<<cur<<endl;
// res.push_back(to_string(pre));
if (cur - pre == diff)
{
diff++;
cout<<"insert "<<cur<<",diff= "<<diff<<endl;
res.push_back(to_string(cur));
}
else // 3456777789
{
// dfindex=j;
int chongfu=diff-1;
if(cur-pre!=chongfu)//也不重复(6777789) 而是679
{
i=j;
diff=0;
}
j++;
}

if(j==13)//cur遍历完了
{
break;
}

// if(j==0&&res.size()<5)//至少够5连
// if(res.size()<5)
// {
// res.clear();
// }
// else if(res.size()>=5)
// {
// path=res;
// //dfindex=j;
// path.push_back("!");
// }


}
//i++;

}
int main()
{
vector<string> a13(13); //不用char,有一个10
for(int i=0;i<13;i++)
{
cin>>a13[i];
}
zhuanhuan1(a13);
sort(a13.begin(),a13.end(),cmp);

for(auto a:a13)
cout<<a<<" ";

cout<<endl;

shunzi(a13);
for(auto a:res)
{
cout<<a<<" ";
}
// zhuanhuan2(a13);
// for(auto a:a13)
// cout<<a<<" ";
//
//2 2 3 3 4 4 5 5 6 6 7 7 8 连队怎么办
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
 void zhuanhuan1(vector<string> &a)
{
for(int i=0;i<a.size();i++)
{
if(a[i]=="J") a[i]="11";
else if(a[i]=="Q") a[i]="12";
else if(a[i]=="K") a[i]="13";
else if(a[i]=="A") a[i]="14";
}
}
void zhuanhuan2(vector<string> &a)
{
for(int i=0;i<a.size();i++)
{
if(a[i]=="11") a[i]="J";
else if(a[i]=="12") a[i]="Q";
else if(a[i]=="13") a[i]="K";
else if(a[i]=="14") a[i]="A";
}
}
bool cmp(string &a,string &b)
{
int c1=stoi(a);
int c2=stoi(b);
return c1<c2;

}

vector<vector<string> >path;
void shunzi(vector<string> a)
{

int diff=1,co=1,flag=0;
int i=0,j=0;

while(i<13)
{
vector<string> res;
flag=0;
diff=1;
while(stoi(a[i])==2) i++;
int fi=stoi(a[i]);

res.push_back(a[i]);

j=i+1;
//int pre=;
while(j<13)
{
while(stoi(a[j])==2) j++;
int cur=stoi(a[j]);

if(cur-fi==diff)
{
j++;
diff++;
if(diff>=5) flag=1;
res.push_back(a[j]);
}
else
{
break;
}
}
i++;
if(flag==1) path.push_back(res);
}
}

哎,都不对,到时候再是哦

最终代码

1.用下标索引比较就好了,0-12分别记录2-A,当然直接从1开始即可

2.for循环作为查找下标,内循环,检查对应下标是否存在值,存在就加入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
void shunzi(vector<string> a)
{
//a[0]是2,不参与
for(int i=1;i<a.size();i++)
{
int co=0;
string t="";
while(i<a.size()&&a[i]!="")
{
t+=a[i]+" ";
co++;
i++;
}
if(co>=5)
{
res.push_back(t.substr(0,t.size()-1));//去掉末尾空格
}

}
if(res.size()==0) cout<<"NO";
else
{
for(auto list:res)
cout<<list<<endl;
}

}
int main()
{
vector<string> a13(13); //不用char,有一个10
for(int i=0;i<13;i++)
{
cin>>a13[i];
}

// zhuanhuan1(a13);
// sort(a13.begin(),a13.end(),cmp);

vector<string> conv(13,"");//比较下标就好啦
for(int i=0;i<13;i++)
{
if(a13[i]=="J") conv[9]="J";
else if(a13[i]=="Q") conv[10]="Q";
else if(a13[i]=="K") conv[11]="K";
else if(a13[i]=="A") conv[12]="A";
else
{
conv[stoi(a13[i])-2]=a13[i];
}
}

shunzi(conv);

// zhuanhuan2(a13);
// for(auto a:a13)
// cout<<a<<" ";
//

}

2 9 J 10 3 4 K A 7 Q A 5 6
3 4 5 6 7
9 10 J Q K A

当然,还有一种情况还有//2 2 3 3 4 4 5 5 6 6 7 7 8 ,这个不是可以分成两个顺子嘛

看来还是得记录下对应值的次数吧。

补充

n台阶

一共有n个台阶,一次走1步或3步,有多少种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int ss(int n)
{
if(n==1) return 1;
else if(n==2) return 1;
else if(n==3) return 2;

return ss(n-1)+ss(n-3);
}
void s1(int n)
{
int step1=1,step2=1,step3=2;
if(n==1||n==2) cout<<1;
if(n==3) cout<<2;
int step4;
for(int i=4;i<=n;i++)
{
step4=step3+step1;
step1=step2;
step2=step3;
step3=step4;
}
cout<<step4;
}

任务调度

顺延思想

题目描述

为了充分发挥GPU算力,
需要尽可能多的将任务交给GPU执行,
现在有一个任务数组,
数组元素表示在这1s内新增的任务个数,
且每秒都有新增任务,
假设GPU最多一次执行n个任务,
一次执行耗时1s
在保证GPU不空闲的情况下,最少需要多长时间执行完成。

输入描述

第一个参数为GPU最多执行的任务个数,取值范围1 ~ 10000
第二个参数为任务数组的长度,取值范围1 ~ 10000
第三个参数为任务数组,数字范围1 ~ 10000

输出描述

执行完所有任务需要多少秒

示例一

输入

1
2
3
3
5
1 2 3 4 5

输出

1
6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void solution(int n,vector<int> a)
{

int time=0,remain=0;
for(int i=0;i<a.size();i++)
{
if(a[i]+remain>n)//这个是上个任务剩下的任务与当前任务总和
{
remain=a[i]+remain-n;//有剩余
time++;//先执行完n个,即过了1s
}
else //总任务数<=n
{
time++;
remain=0;
}
}
//比如4个任务剩下了1个,与下5个任务一起执行
//6-3=3;
time+=remain/n; //处理剩余
if(remain%n>0) time++;
cout<<time;
}

最大资金额

给一堆商品,价格不一,限购n样,有金额m,求最大花费

知识点:全排列组合,其实可以三指针,排序后,一个从最左边开始,一个从最右边和次右边开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void quanpai(vector<int> a,int start);
vector<vector<int>> combines;
void so(vector<int> s,int num)
{
sort(s.begin(),s.end());
if(s.size()<3||s[0]+s[1]+s[2]>num)
{
cout<<"-1";
return;
}
quanpai(s,0);

}
vector<int> cm;

void quanpai(vector<int> a,int start)
{
if(cm.size()==3)
{
combines.push_back(cm);
//cm.clear();不用清除
return;
}
for(int i=start;i<a.size();i++)
{
cm.push_back(a[i]);
//cout<<a[i]<<endl;
quanpai(a,i+1);
cm.pop_back();//移除最后一个
}
}

最小步骤

题目描述

一个正整数数组,设为nums
最大为100个成员
求从第一个成员开始正好走到数组最后一个成员所使用的最小步骤数

要求:

  1. 第一步,必须从第一元素起,且1 <= 第一步步长 < len / 2 (len为数组长度)
  2. 从第二步开始只能以所在成员的数字走相应的步数,不能多不能少,如果目标不可达返回-1,只输出最小的步骤数量
  3. 只能向数组的尾部走不能向回走

输入描述

一个正整数数组,元素用空格分割
数组长度 < 100

输出描述

正整数,最小步数
不存在输出-1

示例一

输入

1
7 5 9 4 2 6 8 3 5 4 3 9

输出

1
2

说明

第一个可选步长选择2
从第一个成员7开始走两步到9
第二步从9经过9个成员到最后

代码

1.第一步只能走1到len/2步,从它开始遍历,step默认为1了

2.递归遍历,walk(int curpos,int lastpos),判断lastpos是否是最后一个索引,是就返回step,如果小于呢,继续递归,从lastpos,走lasepos+a[curpos]步,step++;

3.set记录能够到最后一个索引的所需步骤数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
vector<int> a;
int step;
int walk(int curpos,int lastpos)
{
int num=a[curpos];
if(lastpos==a.size()-1) return step;
else if(lastpos<a.size()-1)
{
step++;
return walk(lastpos,lastpos+num);//在lastpos走num步
}
else return -1;
}
void solu1(vector<int> a)
{
int n=a.size();
set<int> set1;

for(int i=1;i<n/2;i++)
{
step=1;
set1.insert(walk(i,i));
}
if(set1.size()!=1)//不止一条路径,那就将-1去掉
{
auto a=set1.begin();//迭代器访问
set1.erase(*a);
}

for(auto aa:set1)
cout<<aa<<endl;
}

二叉树层次遍历

题目描述

有一棵二叉树
每一个节点用一个大写字母标识
最多26个节点
现有两组字母
分别表示后序遍历(左孩子指向右孩子指向父节点)
中序遍历(左孩子指向父节点指向右孩子)
请输出层次遍历的结果

输入描述

输入为两个字符串
分别为二叉树的后序遍历和中序遍历结果

输出描述

输出二叉树的层次遍历结果

示例一

输入

1
CBEFDA CBAEDF

输出

1
ABDCEF

代码(噢,这是前序遍历输出)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
unordered_map<char,int> mp;
void cengxu(string s1,int posts,int poste,string s2,int mids,int mide)
{
// if(poste==0) return;
if(posts>poste) return;
int rootpos=mp[s1[poste]];

cout<<"root: "<<s2[rootpos]<<endl;



// cengxu(s1,0,rootpos-1,s2,0,rootpos-1);
cengxu(s1,posts,posts+rootpos-mids-1,s2,mids,rootpos-1);
//cengxu(s1,rootpos,poste-1,s2,rootpos+1,mide);
cengxu(s1,posts+rootpos-mids,poste-1,s2,rootpos+1,mide);
}

int main(){
string a,b;
cin>>a;
cin>>b;


for(int i=0;i<b.size();i++)
{
mp[b[i]]=i;
}
cengxu(a,0,a.size()-1,b,0,b.size()-1);
return 0;
}

代码2

先用结构体接收根,左,右

再层序输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<iostream>
#include<vector>
#include<map>
#include<queue>
using namespace std;

struct Node{
char data;
Node* left;
Node* right;
Node(char x):data(x),left(nullptr),right(nullptr){}
};
map<char,int> mp;
Node* create(string s1,int posts,int poste,string s2,int mids,int mide)
{
// if(poste==0) return;
if(posts>poste||mids>mide) return nullptr;
char rootdata=s1[poste];
int rootpos=mp[rootdata];

// cout<<"root: "<<s2[rootpos]<<endl;
Node* root=new Node(rootdata);

root->left= create(s1,posts,posts+rootpos-mids-1,s2,mids,rootpos-1);

root->right=create(s1,posts+rootpos-mids,poste-1,s2,rootpos+1,mide);
return root;
}
void cengxu(Node* root)
{
if(root==nullptr) return;

queue<Node*>q;
q.push(root);

while(!q.empty())
{
Node* cur=q.front();
q.pop();
cout<<cur->data<<endl;

if(cur->left) q.push(cur->left);
if(cur->right) q.push(cur->right);
}
}
int main(){
string a,b;
cin>>a;
cin>>b;


for(int i=0;i<b.size();i++)
{
mp[b[i]]=i;
}
Node* node=create(a,0,a.size()-1,b,0,b.size()-1);
cengxu(node);
return 0;
}

wp

error: cannot call member function ‘Tree* Tree::create(std::__cxx11::string, std::__cxx11::string)’ without object
Tree* t=Tree::create(a,b);

表明您正在尝试调用该类的成员函数而不需要Tree对象创建类的实例。该方法前加个static

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
struct Node{

char data;
Node* left;
Node* right;
Node(char x):data(x),left(nullptr),right(nullptr){}
};
class Tree{
//private:

public:
Node* root;
Tree():root(nullptr){}

static Tree* create(string post,string in){
Tree* res=new Tree();
res->root=create(post,0,post.size()-1,in,0,in.size()-1);
return res;
}
static Node* create(string post,int s1,int e1,string in,int s2,int e2){
if(s1>e1||s2>e2)
{
return nullptr;
}
Node* root=new Node(post[e1]);
for(int i=s2;i<=e2;i++)
{
if(post[e1]==in[i])
{
root->left=create(post,s1,s1+i-s2-1,in,s2,i-1);
root->right=create(post,s1+i-s2,e1-1,in,i+1,e2);
break;
}
}
return root;
}
};
void cengxu(Node* root)
{
if(root==nullptr) return;
queue<Node*> q;
q.push(root);
while(!q.empty())
{
Node* node=q.front();
cout<<node->data<<endl;
q.pop();
if(node->left!=nullptr) q.push(node->left);
if(node->right!=nullptr) q.push(node->right);
}
}
int main(){
string a,b;
cin>>a;
cin>>b;

Tree* t=Tree::create(a,b);
cengxu(t->root);
return 0;
}

代表团坐车

动态规划

题目内容

现在要举行一场会议,有很多代表团参加。但是他们可能在同一个时间到达,而负责接待它们的接待处只有一辆车,现在为了提高车辆利用率,请帮接待员计算可以坐满车的接待方案,输出方案数量

限制:

1.一个团只能上一辆车,并且代表团数量小于汽车容量

2.需要将车坐满

输入描述

第一行 代表团人数,英文逗号分割,代表团数量<30,每个代表团人数<30

第二行 汽车载客量 <100

输出描述

坐满汽车的方案数量,无解输出0

示例

输入

5,4,2,3,2,4,9

10

输出 4

说明:[2,3,5] [2,4,4] [2,3,5] [2,4,4]

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include<iostream>
#include<sstream>
#include<vector>
#include<algorithm>
using namespace std;
int fangan(vector<int> a,int n)
{
// if(n==0) ans[0][0]=1;
// if(n>0) ans[0][n]=0;
int m=a.size();
vector<int> dp(n+1,0);//dp[i] 车容量为i时的方案数
dp[0]=1;
for (int i = 0; i < m; i++)
for(int j=n;j>=a[i];j--)
{
dp[j]+=dp[j-a[i]];
}
return dp[n];




}
int main()
{
/*
5,4,2,3,2,4,9
10
*/

string t;
cin>>t;
stringstream ss(t);
string s;
vector<int> v;
while(getline(ss,s,','))
{
v.push_back(stoi(s));
}
int n;
cin>>n;
// sort(v.begin(),v.end());
int size=v.size();
//int ans[size+1][n+1];//在ans[i][j],nums选取前i个元素,和为j的方案数

// for(auto a:v)
// cout<<a<<" ";
cout<<fangan(v,n);
return 0;
}

贪心:

调整座位

两人之间至少有一空位,0表示座位空闲,1表示占据,不改变原有座位秩序的情况下,还可以安排坐几人

数组desk.length<=2*10⁴

输入:

占座情况

1,0,0,0,1

输出:

根据当前情况,还能坐多少人

1 即desk[2]可以坐一个

1
2
3
4
5
6
7
8
9
for(int i=1;i<a.size();i++)
{
if(a[i-1]==0&&a[i]==0&&a[i+1]==0)
{
a[i]=1;
co++;
}
}
cout<<co;

动规

这个按csdn目录来的,它规定了每道题的知识点,比如动态规划,但有的题,我实在看不出动规,没有用动规就表明了其他方法

跳格子

有n个连续格子,但是首尾相连,组成一个圈,可以从任意格子跳,但:

1.连续的格子不挑

2.不能回头跳

计算最高分

输入 2 3 2

输出 3

输入 1 2 3 1

输出 4 (1+3)

思想:

环状情况,分类讨论

先同时查看两个相邻格子,选较大的那个

第一格子和最后一格子只能选择一个

比较0n-2 和1n-1的最终结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int rob1(vector<int>&nums,int s,int e)
{
int first=nums[s],second=max(nums[s],nums[s+1]);
for(int i=s+2;i<=e;i++)
{
int temp=second;
second=max(first+nums[i],second);//
first=temp;

//相当于dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
}
return second;
}
int rob(vector<int>& nums) {
int len=nums.size();
if(len==1) return nums[0];
else if(len==2) return max(nums[0],nums[1]);

return max(rob1(nums,0,len-2),rob1(nums,1,len-1));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int rob1(vector<int> nums,int s,int e)
{
int len=nums.size();
vector<int> dp(len+2);//定义为从第i间房子抢,最多的钱为dp[i]
// 0~n-1
dp[len]=0;
for(int i=e;i>=s;i--)
{
dp[i]=max(dp[i+2]+nums[i],dp[i+1]);
}

return dp[s];
}
int rob(vector<int>& nums) {
int len=nums.size();
if(len==1) return nums[0];
else if(len==2) return max(nums[0],nums[1]);

return max(rob1(nums,0,len-2),rob1(nums,1,len-1));
}

雨花石

n个不同重量的雨花石,两人平分,重量相等

输入

4 个数n

1 1 2 2 n个不同重量 m[k]<1001

输出

2 从当前雨花石取出的最少数量,or -1

定义dp[i][j],i个石头里,重量为j时至少取走a[i][j]个

a[0][j]=n;(j=1~avg)//记为取无穷个

如果取的重量为m的石头,比j大,等于没操作,a[i][j]=a[i-1][j];

每取走一个重量为m的石头,次数更新为dp[i][j-m]+1

dp[i][j]等于上面两情况取min

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<iostream>
#include<vector>
#include<sstream>
using namespace std;
int pingfen(vector<int> nums,int m)
{
if(m%2!=0) return -1;
int n=nums.size();
int avg=m/2;
vector<vector<int> > dp(n+1,vector<int>(avg+1));//有i个石头,使得总重量为j,至少取a[i][j]次,使得总重量为0
for(int i=1;i<=avg;i++) //取0个石头,使得重量为i的次数
{
dp[0][i]=n;//即无穷次
}
for (int i = 1; i <= n; i++) {//取第i块石头
int num = nums[i - 1];
for (int j = 1; j <= avg; j++) {//i个石头的重量
if (j < num) { //当前石头重量大于j,
dp[i][j] = dp[i - 1][j];//则无法放入这个石头
} else {
/*
两个选择
1.不放入该石头,沿用之前的式子
2.放入该石头,那么总重量减少
+1,说明使用了该石头,石头个数+1
3.要最少的石头个数嘛
*/
dp[i][j] =min(dp[i - 1][j], dp[i - 1][j - num] + 1);
}
}
}
if(dp[n][avg]==n) return -1;
else return dp[n][avg];


}
int main()
{

// string s;
// cin>>s;
int n;
cin>>n;
vector<int> a(n);
int m=0;
for(int i=0;i<n;i++)
{
cin>>a[i];
m+=a[i];
}


/*
1 2 3 1
4
*/
cout<<pingfen(a,m);
return 0;
}

玩牌获取最高分

1.抽取某张牌后,获得对应分数

2.如果不抽牌,其分数为3轮前的总分数;如果当前是第一轮,第二轮或第三轮,总分数置0

输入描述

第一行为n轮的牌面分数

分数值为整数

输出描述

最终选手最高总分数

输入1 -5 -6 4 3 6 -2

输出11

1.一旦不抽牌,为3轮前的总分数,不满3轮不抽牌,则置0

2.所以即使可能遇到负数也要加和,保持最高分

PS:噢,n轮啊,前3轮已经说明了,1,-5,-6 不用另找变量累积了,误以为当某轮结束后,新的轮数从1开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include<iostream>
#include<sstream>
#include<vector>

using namespace std;
int zongfen(vector<int> s)
{
int n=s.size();
vector<int> dp(n+1,0);//i轮时的最高分dp[i]
dp[1]=max(0,s[0]);
int count=0,co=0;
for(int i=2;i<=n;i++)
{
//当前轮选牌后的分数
count=dp[i-1]+s[i-1];
// cout<<count<<endl;
if(i-1<3)//前3轮 2-1=1是第二轮 3-1=2是第三轮
{
dp[i]=max(count,0);
}
else //从第4轮开始
{
dp[i]=max(count,dp[i-3]);
}
cout<<dp[i-1]<<endl;
}
return dp[n];
}
int main(){

vector<int> v;
string s,t;
//cin>>s; //cin遇到空格也会结束
getline(cin,s);
stringstream ss(s);
while(getline(ss,t,' '))
{
v.push_back(stoi(t));
}
cout<<zongfen(v);
return 0;
}

最小步数

坐标0点到坐标点n的最小步数,一次沿横坐标只能向左或向右2步或3步

ps:途径坐标点可为负数

输入坐标点n 4

输出 2

即0->4 先右移两步,再两步

下面这个不对哈,有时候不要考虑那么多,走一步看一步,抓关键点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
dp[0]=0;dp[1]=2;dp[2]=1;dp[3]=1;


for(int i=4;i<=end;i++)
{
//当i=4,向右两次2步
if(i%2==0&&i%3!=0)
{
cout<<"%2"<<endl;
dp[i]=dp[i-2]+1;
}

//当i=9,dp[5]+1,即一次三步
else if(i%2!=0&&i%3==0)
{
cout<<"%3"<<endl;
dp[i]=dp[i-3]+1;
}
//当i=6;
else if(i%2==0&&i%3==0)
{
cout<<"%2 and %3"<<endl;
dp[i]=min(dp[i-2]+1,dp[i-3]+1);
}
// 当i=7 是4了,但正确为3
else if(i%2!=0&&i%3!=0)
{
dp[i]=dp[i-2]+dp[i-3];
}
cout<<"dp["<<i<<"] "<<dp[i]<<endl;
}

用的3越多,步数就越小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int bushu(int end)
{
end=abs(end);
int dp[end+1];//当前坐标i时,从0到i需要移动的最小步数
if(end==1) return 2;
else if(end==2||end==3) return 1;

dp[0]=0;dp[1]=2;dp[2]=1;dp[3]=1;

//用的3越多,步数越小
for(int i=4;i<=end;i++)
{
dp[i]=dp[i-3]+1;
dp[i]=min(dp[i-2]+1,dp[i]);
//dp[i]=min()
cout<<"dp[i]"<<","<<i<<": "<<dp[i]<<endl;
}
return dp[end];
}

最大可靠性(暴力破解)

一个设备N种类型元器件组成(每种类型元器件只要1个,类型编号0~N-1),每个元器件均有可靠性属性reliability

可靠性越高的器件其价格price越贵

而设备可靠性由组成设备的所有器件中可靠性最低的器件决定

给定预算S,买N种器件(每种类型都要一个),不超过预算的情况下,给出能够组成的设备最大可靠性

输入:

500 3//预算和需要种类数

6 //元器件总数

0 80 100

0 90 200

1 50 50

1 70 210

2 50 100

2 60 150

输出

60 //符合的最大可靠性

说明:0的80,1的70,2的60;短板效应,所以是60

暴力枚举,三for()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
struct qijian{
int id;
int rely;
int price;
};

int main(){

int num,n;
cin>>num>>n;

int m;
cin>>m;

vector<qijian> v;
for(int i=0;i<m;i++)
{
qijian a;
cin>>a.id>>a.rely>>a.price;

v.push_back(a);
}
int result=0;
for(int i=0;i<m;i++)
{
for(int j=i+1;j<m;j++)
{
for(int k=j+1;k<m;k++)
{
if(v[i].price+v[j].price+v[k].price<=num&&(v[i].id!=v[j].id)&&(v[i].id!=v[k].id))
{
int tem=min(v[i].rely,v[j].rely);
tem=min(tem,v[k].rely);

result=max(result,tem);
}
}
}
}
cout<<result;
return 0;
}

500 3
6
0 80 100
0 90 200
1 50 50
1 70 210
2 50 100
2 60 150

输出:60

日志首次上报最多积分(读题是难点,双for)

每成功上报一条日志,奖励1分;

每条日志每延迟上报1s,扣1分;

积累日志达到100条,必须立即上报

给出日志序列,根据以上规则,计算首次上报能获得的最多积分数

输入:

按时序产生的日志条数T1,T2…Tn

输出:

首次上报最多能获得的积分数

input:1 98 1

output:98

说明:

input:3 7 40 10 60

output:37

说明:

它每一次的上报是累积的结果,首次上报的意思是,随时都可以上报,那些由于延迟而扣的分,是因为它们之前没有在正确的时刻上报过,所以要统计出到当前的日志总条数,再按延迟减

T1上报,当前3条,+3分

T2上报,当前7条,前1s上报3条,共10条,但T1延迟了1s,-3,故10-3=7分

T3上报,当前40条,前2s上报10跳,共50条,但是T2延迟了1s,-7,T1延迟了2s,-2*3

故50-1*7-2*3=37分

T4上报,当前10条,前3s上报了50条,共60条;但T3延迟1s,-40;T2延迟了2s,-2*7=-14;T1延迟了3s,-3*3=-9;共60-40-14-9=-3分

T5上报,当前60,前4s上报了3+7+40+10=60,共120条;

超过100条了,所以按上报100条起,T4延迟了1s,-10;T3延迟了2s,-2*40=-80,;T2延迟了3s,-3*7=-21;T1延迟了4s,-4*3=12

故100-10-80-21-12=-23

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include<iostream>
#include<sstream>
#include<vector>

using namespace std;
int shangbao(vector<int> a)
{
int n=a.size();
//int dp[n+1];//上第i时刻上报日志条数,首次上报最多的积分数

int res=0;
//dp[0]=0;

for(int i=0;i<n;i++)
{
int result=a[i],co=0;//上报总日志数、待扣除的积分
for(int j=0;j<i;j++)
{
result+=a[j];//累加上报值
co+=(i-j)*a[j];//延迟*对应条数
}

if(result>100) result=100;

result-=co;
res=max(res,result);
}
return res;

}
int main(){

vector<int> v;
string s,t;
getline(cin,s);//以\n结束
stringstream ss(s);
while(getline(ss,t,' '))
{
v.push_back(stoi(t));
}
cout<<shangbao(v);
return 0;
}

积木最远距离

小华和小微一起通过玩积木游戏学习数学。

他们有很多积木,每个积木块上都有一个数字,积木块上的数字可能相同。 小华随机拿一些积木挨着排成一排,请小微找到这排积木中数字相同且所处位置最远的2块积木块,计算他们的距离, 小微请你帮忙替她解决这个问题。

输入描述 第一行输入为N,表示小华排成一排的积木上数字

输出描述 相同数字的积木的位置最远距离;

如果所有积木数字都不相同,请返回-1.

备注: 0 <= 积木上的数字 <= 10^9 1 <= 积木长度 <= 10^5

示例1 输入输出示例仅供调试,后台判题数据一般不包含示例

输入 5

​ 1 2 3 1 4

输出 3

说明: 共有5个积木,第1个积木和第4个积木数字相同,其距离为3.

示例2 输入输出示例仅供调试,后台判题数据一般不包含示例

输入 2

​ 1 2

输出 -1

说明 一共有2个积木,没有积木数字相同,返回-1.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

int juli(vector<int> a)
{
int n=a.size();

//两块相同数字的最远距离
int res=-1;
for(int i=0;i<n;i++)
{
int pre=a[i];
for(int j=i+1;j<n;j++)
{
if(pre==a[j]) res=max(res,j-i);
}
}
return res;
}
int main(){
int n;
cin>>n;
vector<int> a(n);
for(int i=0;i<n;i++)
cin>>a[i];

cout<<juli(a);


return 0;
}

时间复杂度上的优化,使用unordered_map

映射表中的键值,查找时的时间复杂度O(1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int juli(vector<int> a)
{
unordered_map<int,int> mp;//键为数字大小,值为索引
int res=-1;
for(int i=0;i<a.size();i++)
{
auto iter=mp.find(a[i]);//哈希表中查找该键
if(iter==mp.end())//没找到
{
mp.insert(make_pair(a[i],i));//存进去
}
else//找到了
{
if(i-iter->second>res) //距离是否更大
res=i-iter->second;
}
}
return res;
}

补充知识点

1
replace(a.begin(),a.end(),'a','s'); //替换指定字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Check{ 
int x;
int y;
// 定义 operator== 成员函数
bool operator==(const Check& other) const {
return x == other.x && y == other.y;
}

bool operator>(const Check& other) const {
// 比较 x 和 y,根据自定义的逻辑返回结果
// 例如,这里我们假设只要 x 和 y 的和大于对方的和,就认为当前对象大于对方对象
return (x + y) > (other.x + other.y);
}
};

快排

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void quicksort(vector<int>& a,int left,int right)
{
if(left>=right) return;

int first=left;
int last=right;
int key=a[first];
while(first<last)
{
while(first<last&&a[last]>key) last--;
if(first<last) a[first++]=a[last];

while(first<last&&a[first]<=key) first++;
if(first<last) a[last--]=a[first];

a[first]=key;
quicksort(a,left,first-1);
quicksort(a,first+1,right);
}
}
int main(){
vector<int> a={1,8,9,5,3,2,7};
quicksort(a,0,a.size()-1);
for (auto aa:a)
cout<<aa<<" ";
return 0;
}

两数之和最优解

给数组nums,有目标target,找到和为target的两个整数的下标

利用unorder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<iostream>
#include<vector>
#include<map>
using namespace std;
map<int,int> numtoindex;
vector<int> ss(vector<int> a,int t)
{
for(int i=0;i<a.size();i++)
{
int num=t-a[i];
if(numtoindex.find(num)!=numtoindex.end())
{
return {numtoindex[num],i};
}
numtoindex[a[i]]=i;
}
return {};

}
int main(){
vector<int> a={1,8,9,5,3,2,7};
int t=7;
vector<int> res=ss(a,t);
if(res.size()!=0) cout<<res[0]<<","<<res[1];

return 0;
}

3,5

20230802

第一题 100%

就是找宝藏,有n个箱子,删除k个i编号的箱子,使得剩下的箱子个数小于等于原个数的一半

1 1 1 1 3 3 3 6 6 8 10个箱子,1号箱子4个,3号3个,6号2个,8号1个

我用的map,统计i号箱子,及其个数

按个数从大到小排

初始化result=n

遍历mp:map

num=num-mp.second

co++//统计箱子个数

如果满足 num<=n/2 break;

第二题 40%

哎,这个题,待检测目录的大小统计

给目录行数n,待检测目录ID

n行目录,格式为 目录ID(1~200),目录大小 (子目录)

3 2

3 20 ()

2 15 (1)

1 20 (3)

输出

55

这个倒是小问题

主要是

4 2

3 20 ()

2 15 (1,4)

1 20 (3)

4 15 ()

输出

15+20+20+15=70

有逗号了

记不清了

当时想的是递归,但是存储的时候就有问题,我塔喵用vector<string> s,没错,就是把前面的值当做下标i,比如,s[1]=”20 (3)”,塔喵的

做第三题的时候才想到struct,应该用struct的

int id

int size

vector<int> next;

这样存储看着也好点吧

完善代码

思想

1.struct起存储作用

2.map建立映射关系,目录与大小的映射mp1;父目录与子目录的映射mp2

3.递归遍历子目录、子目录的子目录、子目录的子目录的子目录。。。

4.相加的话,直接加mp1[id]就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include<iostream>
#include<vector>
#include<algorithm>
#include<sstream>
#include<map>
using namespace std;
struct mulu{
int id;
int size;
vector<int> nextid;
};

map<int,int >mp1;//来个映射,目录与大小映射
map<int,vector<int> >mp2;//目录与子目录映射
void yingshe(vector<mulu> a)
{

//目录与大小
for(auto tem:a)
{
mp1[tem.id]=tem.size;
}

//目录与子目录
for(auto tem:a)
{
for(int i=0;i<tem.nextid.size();i++)
{
mp2[tem.id].push_back(tem.nextid[i]);
}
}
}

int jisuan(map<int,int> mp1,map<int,vector<int> >mp2,int n)
{
int result=0;
for(auto dir:mp2)//目录与子目录
{
if(dir.first==n)//先将父目录的大小加上来
{
result+=mp1[n];
for(auto dir1:dir.second)//遍历它的子目录
{
//n=dir1;
//result+=jisuan(mp1,mp2,n);//这样不对,应该是要把参数改为&n,所以用下面的吧
result+=jisuan(mp1,mp2,dir1);//递归子目录的子目录
}

}

}
return result;
}
int main(){
/*
3 2

3 20 ()

2 15 (1)

1 20 (3)
*/
int m,n;
cin>>m>>n;
vector<mulu> dir;
for(int i=0;i<m;i++)
{
string t;
mulu a;
cin>>a.id>>a.size;
cin>>t;
t=t.substr(1,t.size()-2);
cout<<"now the t is"<<t<<endl;
if(t.size()==0) a.nextid.push_back(0);//没有值
else
{
if(!count(t.begin(),t.end(),','))//只有一个值
a.nextid.push_back(stoi(t));
else //有多个子目录
{
stringstream ss(t);
string s;
while(getline(ss,s,','))
a.nextid.push_back(stoi(s));
}
// string shuzi="";
// for(int k=0;k<t.size();k++)
// {
// if(isdigit(t[k]))
// shuzi+=t[k];
// else if(k==t.size()-1)//末尾
// {
// a.nextid.push_back(stoi(shuzi));
// }
// else if(t[k]==',')
// {
// cout<<shuzi<<endl;
// a.nextid.push_back(stoi(shuzi));
// shuzi="";
// }

// }
}
dir.push_back(a);
}

yingshe(dir);

// for(auto tem:dir)
// {
// cout<<tem.id<<","<<tem.size<<",";
// for(auto tem1:tem.nextid)
// {
// cout<<tem1<<",";
// }
// cout<<endl;
// }
cout<<jisuan(mp1,mp2,n);
return 0;
}

第三题 60%

这个题,哎,一开始没看懂题,花了不少时间,虽然最后可能看懂了,但没时间了

它是这样的,分销公司自己的钱+下级公司上交的钱(开始这里就没注意),如果满100,上交给它的上级公司15,不够100就算了

比如 199 还是15

​ 200 就是30

注意的是,最终的钱上交给唯一一个公司,称为boss。

且输入不会有回路

输入格式

第一行 行数n

第二行起的n行,格式为分销公司Id 上级公司id 钱

5

1 0 100

2 0 199

3 0 200

4 0 200

5 0 200

输出boss 15+15+30+30+30

这题我觉得有两个关键点

1是找boss

2是子公司上交的钱

先找出boss

比如

5

1 4 200

2 4 200

3 4 200

4 0 200

5 0 200

我的中间阶段的想法是:

上面这三个我用了struct boss啊,int no, int boss, int money

先用vector<boss> a接收了值

先找boss:

这里又引入一个map<int,vector<int> >mp 存储映射关系 键为可能的boss,值为其子公司

上面的例子存储结果为:

0 4,5

4 1,2,3

我到了中期阶段才想到,如果是真正的boss,它是不会出现在值里的

那我引入了一个vector<int> head,存储键,内容为(0,4)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for(auto t:mp)

{

​ head.push_back(t.first);

for(auto tem:mp)

{

for(int i=0;i<tem.second.size();i++)

if(head.back()==tem.second[i]) head.pop_back();

}

}

这里的head[0]应该就是boss了

然后关于计算我也有个map<int,int> mp2

这里就是遍历vector<boss> s,键为tem.boss,值为上交的钱((money/100)*15);

mp2的结果就是

0 30+30=60

4 90

噢~,问题就出现在这里

当时我的中期乃至后期思想是这样的

1
2
3
4
5
6
7
8
9
10
11
12
	for(auto tem:mp2)

{

if(tem.first==head[0])
result+=tem.second;
else
{
result+=(tem.second/100)*15;
}

}

这样得到了 0 60

但是正确的逻辑应该是下面的

4 90

而4 0 200

所以mp2的内容应该是

0 30

4 90

那么其实最终有

4 290

0收到的钱应该是30+(290/100)*15=60

。。。

怪不得,有多少用例答案是这样凑巧对的。

这样说的话,感觉可以套用第二题啊

等等

0 4,5

4 1,2,3

让我们这样分析一下,0公司收获了200+4公司的钱,而4公司收获了600,本身有200,则4公司有

所以0公司收获800/100,嘶不对

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
struct gongsi{

int id;
int shangji;
int money;
};
map<int,int> mp1;
map<int,vector<int> >mp2;
void yingshe(vector<gongsi> gs)
{
//公司与钱
for(auto tem:gs)
{
cout<<tem.id<<endl;
mp1[tem.id]=tem.money;
}

//公司与子公司
for(auto tem:gs)
{
mp2[tem.shangji].push_back(tem.id);
}
}
vector<int> head;//boss作为键,不会出现在值里
void zhaoboss(map<int,vector<int> >mp2)
{
//题目限定 boss就一个
for(auto tem:mp2)
{
head.push_back(tem.first);

for(auto tem1:mp2)
{
if(count(tem1.second.begin(),tem1.second.end(),head.back()))
head.pop_back();
}
}
}
//难点在于从最子公司上交,是boss的直属子公司的话,那还要先等其子公司上交完了再说
/*
公司与子公司
0 1
1 2,3,4
这种处理方法就是,将2,3,4上交给1的钱加起来
再与1本身的钱相加做处理,再交给0,最终才是0所得

所以是1的钱先暂时不交给0,等其子公司忙完再说

那如果是
0 1
1 2,3,4
4 5

先等5交给4
再等4本身的钱+5上交的钱 给1
1本身的钱+2,3,4上交的钱,处理后,给0

怎么搞
关键是上级公司很多,下级公司也很多
每个公司交钱,将自己的+下级公司上交的钱上交
每个上级公司上交给自己的上级前,需要等待下级公司的上交
作为上级公司,怎么知道自己下级公司交完了呢?
1 2 3 4
4 5
*/
map<int,int> mp3;
int jisuan(map<int,int> mp1,map<int,vector<int> >mp2,int boss)
{
for(auto tem:mp2)//子公司对接名单咯
{
//直系公司先不处理,就是直接上交给boss的
if(tem.first!=boss)
{

}

}
}
int main(){
int n;
cin>>n;
vector<gongsi> gs;
for(int i=0;i<n;i++)
{
gongsi a;
cin>>a.id>>a.shangji>>a.money;
gs.push_back(a);
}
// for(auto a:gs)
// {
// cout<<a.id<<a.shangji<<a.money<<endl;
// }

yingshe(gs);

// for(auto a:mp1)
// cout<<a.first<<","<<a.second<<endl;
// for(auto a:mp2)
// {
// cout<<a.first<<" ";
// for(auto aa:a.second)
// {
// cout<<aa<<",";
// }
// cout<<endl;
// }
zhaoboss(mp2);
int boss=head[0];



return 0;
}

完善代码

几天时间后,

思路:

1.两个映射,map<int,vector<int> > mp1 记录公司与子公司

​ map<int,int> 记录公司与自己的钱,注意的是boss需要额外添加

2.找boss,没什么好说的,遍历mp1,如果键出现在了值里,它就不是boss

3.jisuan(mp1,mp2,id),这个计算逻辑:

如果有个底层公司直接归属于上级,直接上交

如果子公司还有子公司,那就遍历,直到子公司是底层为止

比如映射关系如下

0 4,5

4 1,2,3

先找到boss,从boss的子公司开始遍历,是4

那么,开始递归,4的子公司,1有没有子公司呢(mp1的键上有它没有)

没有,则直接交15%给4,修改mp2对应的值,同时将交钱的公司,金钱归0

2,3依次类推

5也可以上交,mp2[5]=0;

最后mp2的情况就是

0 30

4 290

遍历mp2,如果是boss,直接加,如果不是boss,交15%

哎,总结:

对于一个公司来说,其有一个底层公司,那就直接上交15%,但如果其子公司还有子公司,先不接收上交,

最后遍历的时候统一计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
struct gongsi{

int id;
int shangji;
int money;
// vector<int> xiaji;
};
vector<int> head;
map<int,vector<int> > mp1;//公司与子公司映射
//void zhaoboss(map<int,vector<gongsi> > companies)//单纯把键存起来,即可能的boss
map<int,int> mp2;//公司id与money映射

void zhaoboss(map<int,vector<int> > mp1)
{
for(auto tem:mp1)//如果键出现在值里,不是boss
{
head.push_back(tem.first);
for(auto zigs:tem.second)
{
if(head.back()==zigs) head.pop_back();
}
}
}

void jisuan1(map<int,vector<int> >mp1,map<int,int>& mp2,int id)
{
if(mp1.count(id))
{
for(auto zigs:mp1[id])
{
jisuan1(mp1,mp2,zigs);
}
}
else
{
for(auto tem:mp1)
{
if(count(tem.second.begin(),tem.second.end(),id))//底层公司
{
cout<<"Change!"<<endl;
cout<<"当前"<<id<<"是底层公司"<<endl;
cout<<"上级是"<<tem.first<<","<<mp2[tem.first]<<","<<mp2[id]<<endl;
mp2[tem.first]+=mp2[id]/100*15;
mp2[id]=0;
cout<<"上交之后"<<mp2[tem.first]<<endl;
}

}
}
}

int main(){
int n;
cin>>n;
// vector<gongsi> gs(n);
// map<int,vector<gongsi> > company;//上级与下级映射
for(int i=0;i<n;i++)
{
gongsi a;
cin>>a.id>>a.shangji>>a.money;
mp1[a.shangji].push_back(a.id);
mp2[a.id]=a.money;
}

zhaoboss(mp1);
//boss是没有值的
// cout<<head[0]<<endl;
mp2[head[0]]=0;
// for(auto a:mp1)
// {
// cout<<a.first<<" ";
// for(auto a1:a.second)
// {
// cout<<a1<<",";
// }
// cout<<endl;
// }
for(auto a:mp2)
cout<<a.first<<","<<a.second<<endl;
jisuan1(mp1,mp2,head[0]);
// for(auto a:company)
// {
// cout<<a.first<<" ";
// for(auto a1:a.second)
// cout<<a1.id<<"("<<a1.money<<"),";

// cout<<endl;
// }
for(auto a:mp2)
cout<<a.first<<","<<a.second<<endl;
//cout<<head[0]<<","<<jisuan(company,head[0]);

int result=0;
for(auto a:mp2)
{
if(a.first==head[0])
result+=a.second;
else result+=(a.second/100)*15;
}

cout<<head[0]<<","<<result<<endl;


return 0;
}

另一个例子

5
1 0 200
2 1 200
3 1 200
4 1 200
5 4 200

0,0
1,200
2,200
3,200
4,200
5,200

(我打印一下公司的映射关系)

0 1

1 2,3,4

4 5

(从boss开始,0)

Change!
当前2是底层公司
上级是1,200,200
上交之后230
Change!
当前3是底层公司
上级是1,230,200
上交之后260
Change!
当前5是底层公司
上级是4,200,200
上交之后230
0,0
1,260
2,0
3,0
4,230
5,0

最终输出:0,60

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2023-2025 是羽泪云诶
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信