2024集训第一周周记——安详的死去

知识点:

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博客

  • 13
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值