知识点:
1.万能头:
#include<bits/stdc++.h>
2.读取全部:
int n;
while(~scanf("%d",&n){
...
}
3.不用写std::(竞赛内使用)
using namespace std;
4.c++的输入输出(打的更快,有using可以省略)
std::cin>>
std::cout<<
5.一般竞赛中定义大小为1000010大小的静态数组
const int N = 1e6+10;
int n[N];
6.结构体
可使用typedef减少代码量(定义完结构体之后不用再写struct)
结构体内的数据调用用““.””
struct node{
int x, y;
char name;
}ans[N];
7.用clock函数计算运行时间
#include<bits/stdc++.h>
using namespace std;
int main(){
int i, k;
int n = 1e8;
clock_t start, end;
start = clock();
for(int i = 0; i < n; i++){
k++;
}
end = clock();
cout << (double) (end - start) / CLOCK_PER_SEC <<endl;
}
8复杂度:用O()表示
如冒泡排序复杂度O(n^2),哈希算法复杂度O(n)
9.sort函数:类型可以使结构体,左闭右开
bool cmp(int a,int b){ //排序顺序判断,读取类型记得区分,可读取结构体
return a>b; //a>b降序,默认升序
}
int a[1000];
int n=10;
sort(a,a+n,cmp); //将a从a[0]到a[n-1]降序排列
10.空间复杂度S(n)=O(f(n)
11.递归函数:
核心思想:将问题转换成一个递归式
注意点:递归表达式(数学表达式)
递归终止条件
下面是一个牛顿迭代法的递归函数
double sqrNewton1(double x, int k){
if (x == 0) return 0;
double r = x;
for (int i = 0; i < k; ++i){
r = (r + x / r) / 2;
}
return r;
}
12.vector函数:可动态调节的数组。
访问可以直接下表访问,也可以使用迭代器访问。
vector::iterator it;定义迭代器,得到了迭代器it之后,可以通过*it来访问vector中的元素
vector<int> sdt; //无参构造
vector<int> s1(2,10); //构造并初始化2个10
vector<int> s2(s1.begin(), s1.end());//使用迭代器进行初始化构造
vector<int> s3(s1); //拷贝构造
vector常用函数:
push_back(x)就是在vector后面添加一个元素x,复杂度O(1)。
pop_back()用于删除vector的尾元素,复杂度O(1)。
size()用来获得vector中元素的个数,返回的是unsigned类型,不过一般来说用%d不会出很大问题,这一点对所有STL容器都是一样的,复杂度O(1)。
clear()用来清空vector中的所有元素,复杂度O(n)。
insert(it,x)用来向vector的任意迭代器it处插入一个元素x,复杂度O(n)。
erase():复杂度O(n)。
删除单个元素,erase(it)即删除迭代器为it处的元素。
删除一个区间内的所有元素,erase(first,last)即删除[first,last)内的所有元素
清空vector也可以使用v.erase(v.begin(),v.end());
begin();返回第一个元素的迭代器。
end();返回最末元素的下一个位置。
empty();判断vector函数是否为空(返回true时为空)。
13.禁用标准输入输出流的同步,避免开销,提高性能。(频繁输入输出时可以禁用)
ios_base::sync_with_stdio(false);
14.解开cin,cout的绑定,提高性能
cin.tie(nullptr);
cout.tie(nullptr);
15.优先队列:
默认满二叉树,构建最大二叉树。先插入再判断需不需要上浮,每个节点和父节点比较,若大于,互相交换
举例:3 1 6 5 2 4
插入3:堆:3
插入1:堆:3 1
插入6:堆:6 1 3
插入5:堆:6 5 3 1
插入2:堆:6 5 3 1 2
插入4:堆:6 5 4 1 2 3
#include<bits/stdc++.h>
#include<limits.h>
using namespace std;
const int N = 1e3+10;
struct node{
string name,side;
i64 v;//等效int64_t v;精确的64位整数,在<cstdint>头文件中。(一般处理大整数,跨平台(所有整数都是64位),处理精确大小的二进制数据,避免溢出)
friend bool operator<(const node &a,const node &b){//定义优先队列排序规则,按照v的大小排序(如果a.v>b.v,返回true。即从大到小排序)。friend(友元函数,可以访问node中的所有成员。重载<运算符(定义规则,重新定义原有运算符<,只在优先队列中用))
return a.v > b.v;
}
};
priority_queue<node>s,h;//对对象node进行优先队列操作,定义了s和h两个优先队列。根据输入的值,把node对象分别推入s和h队列。
16.栈函数stack:先进后出
常用函数:
empty():判空操作,空返回true
push():将元素加入栈顶
size():返回栈大小
top():返回栈顶元素(不删除栈顶元素)
pop():删除栈顶元素
17,string常用函数:
1(substr函数):字符串截取函数,用于获取字符串的子串:
//str.substr(begin,length),用于截取str中以begin为下标长度为length的字串
string s=“asd”;
s=s.substr(0,1);//结果为a
2(find函数):查找字符串中是否存在该字符:
string s=“asd”;
int a=s.find(‘e’);//如果找到就返回1,否则返回负1
3.(insert):用于添加字符串。
string& insert(size_t pos,const string&str);
// 在位置 pos 处插入字符串 str
string& insert(size_t pos,const char * s);
// 在位置 pos 处插入字符串 s
string& insert(size_t pos,const char * s,size_t n);
// 在位置 pos 处插入字符串 s 的前 n 个字符
string& insert(size_t pos,size_t n,char c);
// 在位置 pos 处插入 n 个字符 c
4.(erase函数):用于作字符串删除操作
//str.erase(begin,length);//用于删除str的从begin下标开始长度为length的字符串。
string str=“abc”;
str.erase(1,1);//结果为ac
5.(replace函数):用来对字符串的字串作替换处理
//str.replace(begin,length,string s);用于把下标为begin,长度为length的字串替换为s。
string str=“abc”;
str=str.replace(1,2,“lk”);//str=“alk”。
//str.replace(s.begin(),s.begin()+3,“aaa”);给出两个起始位置和终点,把其中的字符串替换为"aaa"
// str.replace(1, 3, “123456”, 3, 5);用"123456"子串的下标[3,5]替换str的[1,3]的位置.
//str.replace(1, 3, 5, ‘a’); //用5个’a’代替str的[1,3]的位置.
6.(reverse()函数):翻转字符串
string s=“lklk”;
reverse(str.begin(),str.end());//翻转整个字符串
reverse(str.begin(),str.begin()+n);//也可以翻转字符串的前n个字符;
题解:
F - 1D Pawn
题意:
有𝑁个方块,编号为Square1, Square2, …, Square𝑁, 从左到右排成一行。
此外,有𝐾个棋子。最左边的第𝑖i个棋子最初放在Square𝐴𝑖上。
现在,我们将对它们执行𝑄次操作。 第𝑖次操作如下:
- 如果从左边数第𝐿𝑖个棋子在最右边的方块上,则不执行任何操作。
- 否则,如果右边下一个方块上没有棋子,则将从左边数第𝐿𝑖个棋子向右移动一个方块;如果有棋子,则不执行任何操作。
打印出第𝑖=1,2,…,𝐾次操作结束后,从左边数第𝑖个棋子所在的方块的索引。
思路:
模拟,用vis数组记录这个位置是否有棋子,pos数组记录第i个棋子的位置。
代码:
#include<iostream>
using namespace std;
const int N=2e5+10;
int pos[N],vis[N];
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);//通过清除绑定键的缓存区来自处理大量数据时减少时间开销(同cin.tie()和cin.tie())
int n,k,q;
cin>>n>>k>>q;
for(int i=1;i<=k;i++){
cin>>pos[i];
vis[pos[i]]=1;
}
while(q--){
int id;
cin>>id;
int p=pos[id];
if(vis[p+1]==0&&p+1<=n) vis[p]=0,vis[p+1]=1,pos[id]=p+1;
}
for(int i=1;i<=k;i++) cout<<pos[i]<<" \n"[i==n];
return 0;
}
一开始没看懂题目什么意思,看懂之后其实还是简单的。
E-新汉诺塔
题目
Description
设有几个大小不等的中空圆盘,按从小到大的顺序从1到n编号。将这,个圆盘任意的迭套在三根立柱上,立柱的编号分别为 4,B,C,这个状态称为初始状态。
现在要求找到一种步数最少的移动方案,使得从初始状态转变为目标状态。移动时有如下要求:
。一次只能移一个盘:
。不允许把大盘移到小盘上面。
Input
第一行一个整数,状态中圆盘总数 n。
接下来三行每行若干个整数,分别代表初始状态下 A,B,℃ 柱子上的圆盘从上到下的编号,如果只有一个数0就代表这根柱子上没有数。
接下来三行每行若干个整数,分别代表目标状态下 A,B,℃ 柱子上的圆盘从上到下的编号,如果只有一个数0就代表这根柱子上没有数。
Output
若干行每行一个字符串,格式为 moveI from p to Q ,代表一个移动的操作。
接下来一行一个整数,代表从初始状态到目标状态的最少步数。
思路:
我们可以考虑一下怎么才从初始的变到最终的答案. 首先我们可以想到需要从大到小依次归位,因为如果先归位小的圆盘.如果后续还需要归位大的圆盘,我们需要对其进行移动,因此我们需要从大到小归位.我们可以把归位的过程简单分为两步 1 把所有在目标点上的比该圆盘小的数移开, 2 把该圆盘移动到目标点; 重复操作 但这就是最优的吗并不一定,因为我们起初的部分是一个恢复成一个常规汉诺塔操作的过程,因此我们实际上并不止这一种恢复常规汉诺塔的方法,我们可以换一种方法 可以简单分为四步 1.把所有在另一个非目标点的圆柱上的比该圆盘小的数移开 2.该圆盘移到另一个非目标点的圆柱上, 3把所有在目标点上的比该圆盘小的数移开, 4把该圆盘移动到目标点;我们会发现两种方法的12和34是相同的,因此我们只需要对最大的进行操作就可以了,然后先进行一次不输出结果的递归,查看各自所需要的步数,选择较小的输出方法
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n + 1), b(n + 1);
for (int i = 0; i < 3; i++) {
int x;
cin >> x;
for (int j = 0; j < x; j++) {
int y;
cin >> y;
a[y] = i + 1;
}
}
for (int i = 0; i < 3; i++) {
int x;
cin >> x;
for (int j = 0; j < x; j++) {
int y;
cin >> y;
b[y] = i + 1;
}
}
int flag = 0;
int mode = 0;
vector<int> t(n + 1);
int cnt[2] = {0};
auto dfs = [&](auto self, int w, int s, int e) -> void {
if (s == e) return;
for (int i = w - 1; i >= 1; i--) {
self(self, i, t[i], 6 - s - e);
}
if (flag) cout << "move " << w << " from " << char(s + 'A' - 1) << " to " << char(e + 'A' - 1) << '\n';
else cnt[mode]++;
t[w] = e;
};
mode = 0;
for (int i = 1; i < a.size(); i++) {
t[i] = a[i];
}
for (int i = n; i >= 1; i--) dfs(dfs, i, t[i], b[i]);
mode = 1;
for (int i = 0; i < a.size(); i++) {
t[i] = a[i];
}
dfs(dfs, n, t[n], 6 - t[n] - b[n]);
for (int i = n; i >= 1; i--) dfs(dfs, i, t[i], b[i]);
flag = 1;
for (int i = 1; i < a.size(); i++) {
t[i] = a[i];
}
if (cnt[0] < cnt[1]) {
for (int i = n; i >= 1; i--) dfs(dfs, i, t[i], b[i]);
} else {
dfs(dfs, n, t[n], 6 - t[n] - b[n]);
for (int i = n; i >= 1; i--) dfs(dfs, i, t[i], b[i]);
}
cout << min(cnt[0], cnt[1]);
}
其实不难,就是做起来很烦,要调用大量for循环
C-简单计算器
题目:
读入一个只包含 +, -, *, / 的非负整数计算表达式,计算该表达式的值。
inpuit
测试输入包含若干测试用例,每个测试用例占一行,每行不超过200个字符,整数和运算符之间用一个空格分隔。没有非法表达式。当一行中只有0时输入结束,相应的结果不要输出。
output
对每个测试用例输出1行,即该表达式的值,精确到小数点后2位。
思路:
用自定义函数get:利用乘十相加得出数字。利用两个栈of和stk分别存储字符和数字,利用空格分割字符和数字,乘除运算把两个数合成一个数,这样只剩下加减,随后加减得出最后的数
代码:
#include<bits/stdc++.h>
#include<limits.h>
#define endl '\n'
#define i64 long long
#define fi first
#define se second
#define Endl endl
#define END endl
#define mod3 998244353
#define mod7 1000000007
#define de(x) cerr << #x <<" "<<x <<" ";
#define deb(x) cerr << #x <<" "<<x <<endl;
using namespace std;
double get(string s,int &i){
double res = 0;
for(;;i++){
if(s[i] <= '9' && s[i] >= '0'){
res = res * 10 + (int)(s[i] - '0');
}else{
break;
}
}
return res;
}
int main(){
string s;
while(getline(cin,s)&& s!="0"){
int n = s.size();
stack<char>of;
stack<double>stk;
double cnt = 0;
for(int i = 0;i < n;i ++){
if(s[i]>='0' && s[i]<='9'){
cnt = cnt * 10 + s[i] - '0';
// deb(cnt)
if(i+1==n ||s[i+1] == ' '){
stk.push(cnt);
cnt = 0;
}
}else if(s[i] == '+' || s[i] == '-'){
of.push(s[i]);
}else if(s[i] == '*'){
i += 2;
double res = get(s,i);
res = 1.0 * res * stk.top();
stk.pop();
stk.push(res);
}else if(s[i] == '/'){
i+=2;
double res = get(s,i);
res = 1.0*stk.top() / res;
stk.pop();
stk.push(res);
}
}
vector<double>num;
vector<char>oof;
while(!stk.empty()){
num.push_back(stk.top());
stk.pop();
}
while(!of.empty()){
oof.push_back(of.top());
of.pop();
}
n = oof.size();
reverse(num.begin(),num.end());
reverse(oof.begin(),oof.end());
double ans = num[0];
for(int i = 0;i < n;i ++){
if(oof[i] == '+')ans += num[i+1];
else{ans-=num[i+1];}
}
printf("%.02lf\n",ans);
while(!stk.empty())stk.pop();
}
return 0;
}
没有考虑乘除运算中将两个数组合成一个数,导致加减乘除的优先级混乱,然后思路紊乱做不出题。
G-Function
题目:
题解:
记忆化搜索,算出一次w(x,y,z)后就不用再算了
代码:
#include <cstdio>
#define LL long long
LL dp[25][25][25];
LL w(LL a, LL b, LL c)
{
if(a <= 0 || b <= 0 || c <= 0) return 1;//两个特判,题意里都有的。
if(a > 20 || b > 20 || c > 20) return w(20, 20, 20);
if(a <b && b < c)//情况一,每一个没有储存过的“w”值全部储存,如果有就直接调用。
{
if(dp[a][b][c-1] == 0)
{
dp[a][b][c-1] = w(a, b, c-1);
}
if(dp[a][b-1][c-1] == 0)
{
dp[a][b-1][c-1] = w(a, b-1 ,c-1);
}
if(dp[a][b-1][c] == 0)
{
dp[a][b-1][c] = w(a, b-1, c);
}
dp[a][b][c] = dp[a][b][c-1] + dp[a][b-1][c-1] - dp[a][b-1][c];
}
else//同上
{
if(dp[a-1][b][c] == 0)
{
dp[a-1][b][c] = w(a-1, b, c);
}
if(dp[a-1][b-1][c] == 0)
{
dp[a-1][b-1][c] = w(a-1, b-1 ,c);
}
if(dp[a-1][b][c-1] == 0)
{
dp[a-1][b][c-1] = w(a-1, b, c-1);
}
if(dp[a-1][b-1][c-1] == 0)
{
dp[a-1][b-1][c-1] = w(a-1, b-1, c-1);
}
dp[a][b][c] = dp[a-1][b][c] + dp[a-1][b][c-1] + dp[a-1][b-1][c] - dp[a-1][b-1][c-1];
}
return dp[a][b][c];
}
int main()
{
LL a, b, c;
while(scanf("%lld%lld%lld", &a, &b, &c))//无限输入,直到“-1 -1 -1”
{
if(a == -1 && b == -1 && c == -1) return 0;//-1 -1 -1就直接结束,不运算了。
printf("w(%lld, %lld, %lld) = ", a, b, c);
printf("%lld\n", w(a, b, c));
}
}
时间不够,做到后面脑子不够用,逻辑紊乱导致没有思路(简称大脑宕机了)
C-古希腊掌管原神的神
题目:
思路:
,考虑最坏情况,即变换之神说的都是假的。真的必须大于假的部分,所以a>b+c。
当a=1时,则只有一个神,那个神就是原神。
当a>1时,则最坏有b+c次假神,需要b+c+1次真神,所以需要2*(b+c)+1次。
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a, b, c; cin>>a>>b>>c;
if(a > b+c)
{
if(a == 1) cout<<"YES"<<endl<<0<<endl;
else cout<<"YES"<<endl<<2*(b+c)+1<<endl;
}
else cout<<"NO"<<endl;
return 0;
}
材料来源:
vector函数:vector容器 常用函数_vector函数-CSDN博客
string函数:string类常用函数_string函数-CSDN博客