美文网首页
CSP第二题思路总结

CSP第二题思路总结

作者: 加油11dd23 | 来源:发表于2020-06-28 11:04 被阅读0次

CCF-CSP考试的时候总是会犯一些错误,并且自己思路太单调想不到好方法,这里简单总结一下相关的方法和技巧。

1、201912-2回收站选址

(1)自己最开始的思路就是单纯的模拟,后来发现有负数,不了了之。

(2)一个技巧是建立结构体数组,这样坐标就可以为负数。另外遍历的时候不是遍历所有的地址,而实只遍历含有垃圾的地方,这样会大大降低时间复杂度。

(3)有一些其他的技巧用到了STL中的Map和set。

2、201909-2小明种苹果

这道题用数组就可以模拟,难点在于如何统计相邻连续三棵树发生苹果掉落的组数。

(1)这道题应该是考试的时候第二题拿分最高的一道了,拿了50分,课后做了90分,当时不知道分是怎么丢的,现在想来是在E的理解上与题目有偏差。认为只有三棵树时,连续三棵树有苹果掉落的组数最大应该为1,其它2种情况应该是重复的。而题目意思实际上应该是3。

(2)高超的模拟是设立flag数组,这样发生掉落的苹果树数和连续三组发生掉落的苹果树数都好计算。同时苹果总数本身就好计算,不多说。

3、201903-2二十四点

(1)这道题考试的时候真的是没思路,只能强硬模拟。最后竟然也拿了分。【后续发现其实就是简化版的中缀表达式求值】

(2)用队列或栈的特性实现四则运算。

顺序遍历表达式,如果遇到的是数字,压入数字栈,如果遇到的是符号,压入符号栈。由于x、/的优先级较高,所以当遇到乘法和除法时,直接计算结果,并把结果压入数字栈。还由于,减法的结合性是从左向右的,所以我们做一个简单地转换,将所有的减法变为加法。最后数字栈中的数就是结果了。【都是加法就可以从右往左算了,真的精妙】”

<code>

#include<cstdio>

#include<cstring>

#include<stack>

using namespace std;

int n;

char str[10];

stack<int> num;

stack<char> sign;

int main(){

scanf("%d",&n);

getchar(); //读取留在缓冲区的换行符

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

gets(str);

while(!num.empty()) num.pop(); //清空栈

while(!sign.empty()) sign.pop();

int j=0;

while(j<strlen(str)){

if(str[j]>'0' && str[j]<='9'){

num.push(str[j]-'0');

}

else{

if(str[j]=='+'){

sign.push('+');

}

else if(str[j]=='-'){ //将减法转换成加法

num.push((str[j+1]-'0')*(-1));

sign.push('+');

j++;

}

else if(str[j]=='x'){ //直接计算乘法

int lhs=num.top();

num.pop();

num.push(lhs*(str[j+1]-'0'));

j++;

}

else if(str[j]=='/'){ //直接计算除法

int lhs=num.top();

num.pop();

num.push(lhs/(str[j+1]-'0'));

j++;

}

}

j++;

}

while(!sign.empty()){ //计算剩余的加法

int rhs=num.top();

num.pop();

int lhs=num.top();

num.pop();

sign.pop();

num.push(lhs+rhs);

}

int ans=num.top();

if(ans==24) printf("Yes\n");

else printf("No\n");

}

return 0;

}

</code>

此外,本道题还可以构造表达式树。

4、201812-2小明放学

(1)这道题自己的思路只有10分,也是强硬模拟,不过至今不知道是那里出问题,只能强硬参考别人的思路了。

(2)一些人利用取余运算和数组运算实现循环变换,这些自己当时没有想到,毕竟靠的有些生硬。

5、201809-2买菜

(1)这道题自己也是分类讨论,但是分不高。

(2)有些人利用给每个时间段建立数组,每当该时间段有人时就加一,最后计算有2个人的时间段有哪些,这种方法真的精妙。

(3)有些人不利用分类讨论,利用数学公式遍历所有时间段min(a,b) -max(c,d)。

6、201803-2碰撞的小球

(1)自己最开始模拟的时候没想清楚序号和位置的关系,思路很混乱,不知道应该怎么实现。

(2)有些人不考虑序号问题(因为最终小球的顺序一定是按放入的顺序排的),直接用pos[i]记录每个小球的位置,step[i]记录每个小球运动的方向。遇到墙改变方向。每次移动完之后判断小球直接有没有发生碰撞,改变方向的情况。

(3)有些人利用结构体数组存储每个小球的序号,位置和移动方向。模拟的时候比较简单,按位置排序后,移动每一个小球。最后输出的时候按序排好序号输出每一个小球。

7、201712-2 游戏 

(1)做的时候只拿了40分,利用循环来模拟。

(2)一些人利用队列模拟,很精妙。“起始时将所有人的编号压入队列,设置一个代表当前报的数字的变量,每次从队列弹出一个编号作为当前报数的人,查看其报的数字是否应该被淘汰,如果没有被淘汰将这个人编号重新压入队列中。当队列中只剩余一个人的编号时,这个人即为胜利者,输出即可。”

<code>

#include<bits/stdc++.h>

using namespace std;

int main(){

    int N,K;

    scanf("%d%d",&N,&K);

    queue<int>q;

    for(int i=1;i<=N;++i)//将所有人编号压入队列 

        q.push(i);

    int num=1;//当前的报数 

    while(q.size()>1){

        int t=q.front();//获取当前报数的人的编号 

        q.pop();

        if(!(num%K==0||num%10==K))//如果既不是K的倍数,末位也不为K 

            q.push(t);//将这个人编号加入队列中 ,表示没有被淘汰 

        ++num;//递增当前报数

    }

    printf("%d",q.front());//输出最后获胜的人的编号 

</code>

8、201709-2 公共钥匙盒

(1)做的时候暴力模拟40分。

(2)定义一个Key类,包含钥匙编号、当前时间、是还钥匙还是取钥匙。为了使用优先级队列,还需重载<运算符,注意,优先级队列默认队首元素为最大的元素,所以在定义<运算符时要与题目要求的情况相反,才能保证满足题目要求的元素位于队首。之后的操作就是在读取数据时将相应的Key类对象压入队列,之后一一弹出,并对记录挂钩上钥匙编号的数组进行相关操作就可以了。

<code>

#include<bits/stdc++.h>

using namespace std;

struct Key{//定义Key类

int num;//钥匙编号

int time;//当前时间

bool borrow;//表示是取钥匙还是还钥匙 ,true表示取,false表示还

Key(int n,int t,bool b):num(n),time(t),borrow(b){}//构造函数

bool operator<(const Key&k)const{//重载<运算符 ,注意优先级队列默认以最大元素为队首元素

if(this->time!=k.time)//以时间最早的位于队首

return this->time>k.time;

else if(this->borrow!=k.borrow)//先还钥匙再取钥匙

return this->borrow&&!k.borrow;

else//以钥匙编号最小的位于队首

return this->num>k.num;

}

};

priority_queue<Key>pq;//优先级队列

int main(){

int N,K;

scanf("%d%d",&N,&K);

int a[N+1];//表示当前挂钩上钥匙顺序的数组

for(int i=0;i<N+1;++i)

a[i]=i;

for(int i=0;i<K;++i){//读取数据

int a,b,c;

scanf("%d%d%d",&a,&b,&c);

pq.push(Key(a,b,true));

pq.push(Key(a,b+c,false));

}

while(!pq.empty()){//队列不空,继续循环

Key k=pq.top();

pq.pop();

if(k.borrow){//如果是取钥匙

int i=1;

while(a[i]!=k.num)//查找取的钥匙编号在数组中的位置

++i;

a[i]=-1;//令该位置处钥匙编号为-1,表示该挂钩没有挂钥匙

}else{//如果是还钥匙

int i=1;

while(a[i]!=-1)//查找数组中第一个没挂钥匙的挂钩

++i;

a[i]=k.num;//令该位置处钥匙编号为还的钥匙编号

}

}

for(int i=1;i<N+1;++i)//输出

printf("%d ",a[i]);

}

</code>

9、20170302-学生排队

(1)暴力模拟

(2)在移动过程中,不可避免地要涉及到元素在中间位置的插入和删除,所以用stl中的list容器是最好的。可利用暴力搜索的方法遍历整个链表找到要移动的元素的位置,并将该元素删除;利用迭代器的递增递减运算找到要移动之后的位置,将该元素插入。注意,list的迭代器不支持+、- 运算符,但支持++、-- 运算符。

<code>

#include<bits/stdc++.h>

using namespace std;

int main(){

int N,M;

scanf("%d%d",&N,&M);

list<int>l;//存储学号的链表

for(int i=0;i<N;++i)//将所有学号加入链表中

l.push_back(i+1);

while(M--){

int a,b;

scanf("%d%d",&a,&b);//读取移动的学号,和移动的长度

list<int>::iterator i=l.begin();

while(*i!=a)//遍历链表查找要移动的学号在链表中的位置

++i;

i=l.erase(i);//删除该元素

while(b<0){//找到移动后的位置

--i;

++b;

}

while(b>0){

++i;

--b;

}

l.insert(i,a);//插入该元素

}

for(list<int>::iterator i=l.begin();i!=l.end();++i)//遍历输出

printf("%d ",*i);

return 0;

}

</code>

相关文章

  • CSP第二题思路总结

    CCF-CSP考试的时候总是会犯一些错误,并且自己思路太单调想不到好方法,这里简单总结一下相关的方法和技巧。 1、...

  • CSP策略及绕过方法

    XSS的时候经常要绕过CSP,这里总结一下 CSP策略 一个CSP头由多组CSP策略组成,中间由分号分隔,就像这样...

  • csp202012csp十二月第二题

  • letcode[058] 最后一个单词的长度

    题目地址:最后一个单词的长度 思路1:自拟思路——使用split分列 这个题就很基础了。总结: 和排名第二的代码只...

  • 链表题思路总结

    链表相关的题,给我的第一印象就是指来指去,一个不小心,你就可能晕了。第二印象就是考虑各种边界条件,很烦神烦。第三印...

  • 第六十章 CSP的常见问题 - 如何结束CSP会话,CSP会话超

    第六十章 CSP的常见问题 - 如何结束CSP会话,CSP会话超时 如何结束CSP会话? 若要结束CSP会话,请在...

  • 两整数之和(不使用+、-)

    (力扣371题)思路:说实话看到这个题没啥思路,最后只能总结,无进位加法使用异或运算计算得出,进位结果使用与运算和...

  • 刷题编程思路总结

    如何想出解题编程思路和算法? 观点1 在假设题主懂编程语言基础语法的前提下,我提供以下思路1.首先从问题的基本定义...

  • LeetCode刷题思路总结

    回溯 今天自己去解了一道组合的问题,从循环暴力慢慢走到回溯的思路,今天就挺明白了。 凡是排列组合问题,正常的循环处...

  • Go基础——channel

    CSP 要想理解 channel 要先知道 CSP 模型。CSP 是 Communicating Sequenti...

网友评论

      本文标题:CSP第二题思路总结

      本文链接:https://www.haomeiwen.com/subject/ltsifktx.html