中南大学第十一届大学生程序设计竞赛总结
2017-05-02 01:34
281 查看
1.Tricky数
题目要求链接分析
要注意审题,注意:这种数字在不重复利用每一位的条件下,可以提取出k(k>0)个子序列,而且保证提取出这k个子序列都是”233”,且不再剩余任何数字。当初我就是没有认真审题就导致花费了很长时间都没有做出来,后来又读了几遍题,才真正理解题意。
题意基本就是给出一个最大长度为1000的字符串,让你判断该字符串是否可以全部分解为233的子序列,且最后不在包含任何数字。
解题思路1:
从左向右循环考虑每一个2,由于我们是从左向右来考虑,因此最佳策略就是选择这个2之后最近的没有被配对的两个3与之配对,并将它们标记为已经配对。
最后再检查是否所有字符都被打上已配对的标记
我的代码
#include<iostream> #include<cstring> using namespace std; bool a[1000]; //保存标记位 char c[1000]; //保存字符串 int main() { int T; scanf("%d",&T); while(T--) { bool flag = true; memset(a,0,sizeof(a)); scanf("%s",c); int len = strlen(c); /*从左向右循环考虑每一个2,由于我们是从左向右来考虑, 因此最佳策略就是选择这个2之后最近的没有被配对的两个3与之配对, 并将它们标记为已经配对。*/ for(int i=0; i<len; i++) { int location1=-1,location2=-1; if(c[i] == '2') { for(int j=i+1; j<len; j++) { if(c[j]=='3' && a[j]==0) { if(location1 == -1) { location1 = j; continue; } location2 = j; a[i] = 1; a[location1] = 1; a[location2] = 1; break; } } } } //检查是否所有字符都被打上已配对的标记 for(int i=0; i<len; i++) { //printf("%d\n",a[i]); if(a[i]==0) flag = false; } if(flag){ printf("Yes\n"); }else{ printf("No\n"); } } return 0; }
解题思路二:
参考我们学校另一位同学的思想,点击查看
我才知道可以在O(N)时间复杂度内完成。就是从左到右扫描字符串,分别记录‘2’和‘3’出现的次数,进行相应操作即可。
代码
#include<iostream> #include<cstring> using namespace std; char a[1000]; int main() { int T; scanf("%d",&T); while(T--) { scanf("%s",a); int len = strlen(a); bool flag = true; int cnt2 = 0, cnt3 = 0; for(int i=0; i<len; i++) { if(a[i] == '2') { cnt2++; }else if(a[i] == '3') { if(cnt2 == 0) { flag = false; break; } if(cnt3 == 1) { cnt2--; cnt3 = 0; }else{ cnt3++; } }else { flag = false; break; } } if(cnt2!=0 || cnt3!=0) flag = false; if(flag){ printf("Yes\n"); }else{ printf("No\n"); } } return 0; }
题目评价:
考查内容: 基本字符串操作、贪心
时间复杂度:O(n^2)、O(n)
题目难度: ☆
2.锋芒不露
题目要求分析
题意不太好理解,需要多读几遍。
其实题意就是让左右剑两两组合,使得组合中和的最大值尽可能小。那么可以想到,要使和的最大值尽可能小,那么必然是左剑的最小值和右剑的最大值组合,然后左剑的次小值和右剑的次大值组合,这样依次进行两两组合,那么这些和中最大的值就是我们所要求的答案。
如果左手剑的数值是a[1,2,3,…,n] , 右手剑的数值是b[1,2,3…,n]。我们显然可以想到,先把两个数组a[],b[]排序,然依次将a[i]和b[n-i-1]求和,最后max{a[i]+b[n-i-1]}就是所求的值。且不说排序所要花费的时间,单单是对1-n时,数据比较n次,时间复杂度就是O(N^2)。而N是1e5级别的,N^2就是1e10。一般来说,1e8运算所需要的时间是1秒钟,这样显然会超时。
那么该怎么办呢?注意分析题,我们可以发现,剑的值不超过100,所以可以用100大小左右数组记录每一个值存在多少把剑,左手从小到大,右手从大到小,不断交替减掉匹配的次数,更新当前匹配的最大值。基本就是一种桶排序的思想。
我的代码
//#include<iostream> #include<cstdio> #include<cstring> using namespace std; //left[i]表示左手剑中攻击力值为i的个数。right[j]表示右手剑中攻击力值为j的个数 int left[101],right[101]; int main() { int N; while(~scanf("%d",&N)) { if(N == 0) break; //memset函数给int类型数组赋值只能赋为-1或0,因为 该函数是逐字节赋值的 memset(left,0,sizeof(left)); memset(right,0,sizeof(right)); int x,y; for(int k=1; k<=N; k++) { scanf("%d%d",&x,&y); left[x]++; right[y]++; int maxSum = 0; int match = 0; int location1 = 1, location2 = 100; int cishu1 = left[location1], cishu2 = right[location2]; while(match != k) { if(cishu1 == 0) { cishu1 = left[++location1]; continue; } if(cishu2 == 0) { cishu2 = right[--location2]; continue; } if(location1+location2 > maxSum ) { maxSum = location1+location2; } if(cishu1 == cishu2) { match += cishu1; cishu1 = left[++location1]; cishu2 = right[--location2]; }else if(cishu1 < cishu2) { match += cishu1; cishu2 -= cishu1; cishu1 = left[++location1]; }else if(cishu1 > cishu2) { match += cishu2; cishu1 -= cishu2; cishu2 = right[--location2]; } } printf("%d\n",maxSum); } printf("\n"); } }
我的代码这里把
#include<iostream>注释掉了,换成了
#include<cstdio>是因为iostream中已经定义过了left和right。而cstdio头文件中没有。
题目评价
考查内容:贪心,计数
时间复杂度 O(n*100)
题目难度:☆☆☆
3.复盘拉火车
题目要求题目链接
题目分析
模拟整个游戏过程即可。
由于一个玩家“吃”掉的牌会按照原来的顺序放入自己牌堆的最后面,符合队列“先进先出”的特点,因此可以用队列来实现“吃牌”操作。
其次可以开一个数组来记录每种牌在桌面上出现的位置(蕴含着该牌已经在桌面上出现),这样当再次放牌时就知道了“吃牌”区间。
我的代码
#include<iostream> #include<queue> #include<string> using namespace std; const int LEN = 13; queue<string> GJ; queue<string> XS; string a[LEN]; //a[]保存桌面上的牌 int main() { int T; cin>>T; int N1,N2,K; string c; //初始化a[] for(int i=0; i<LEN; i++) { a[i] = "0"; //"0"表示什么都没有 } while(T--) { //读入数据,并放到相应的队列中 cin>>N1; //scanf("%d",&N1); for(int i=0; i<N1; i++) { cin>>c; //scanf("%s",&c[0]); GJ.push(c); } cin>>N2; //scanf("%d",&N2); for(int i=0; i<N2; i++) { cin>>c; //scanf("%s",&c[0]); XS.push(c); } //进行K次放牌 scanf("%d",&K); for(int i=0; i<K; i++) { if(i%2 == 0) { c = GJ.front(); GJ.pop(); for(int j=0; j<LEN; j++) { //找到了相同的牌 if(c == a[j]) { for(int k=j; k<LEN && a[k]!="0"; k++) { GJ.push(a[k]); a[k] = "0"; } GJ.push(c); break; } //没有找到相同的牌 if(a[j] == "0") { a[j] = c; break; } } }else{ c = XS.front(); XS.pop(); for(int j=0; j<LEN; j++) { //找到了相同的牌 if(c == a[j]) { for(int k=j; k<LEN && a[k]!="0"; k++) { XS.push(a[k]); a[k] = "0"; } XS.push(c); break; } //没有找到相同的牌 if(a[j] == "0") { a[j] = c; break; } } } } //输出,同时顺便清空GJ、XS、a[]中的牌 cout<<"Deck:"; //printf("Deck:"); for(int i=0; i<LEN&&a[i]!="0"; i++) { cout<<" "<<a[i]; //printf(" %s",a[i]); a[i] = "0"; } cout<<endl; //printf("\n"); cout<<"GJ:"; //printf("GJ:"); while(!GJ.empty()) { cout<<" "<<GJ.front(); //printf(" %s",GJ.front()); GJ.pop(); } cout<<endl; //printf("\n"); cout<<"XS:"; //printf("XS:"); while(!XS.empty()) { cout<<" "<<XS.front(); //printf(" %s",XS.front()); XS.pop(); } cout<<endl; //printf("\n"); cout<<endl;// printf("\n"); } return 0; }
我用scanf和printf输入输出字符串有问题。。一直搞不懂问题出在哪儿。。
题目评价:
考查内容:基本数据结构的使用
时间复杂度:O(n)
题目难度:☆ ☆
我的体会
就这三道题,其实都不难,但比赛的时候我只做出来一道,而且是自己错了好几次才做出来。
我觉得首先要认真读题,除了要真正理解题目的意思,还要注意一些数据的范围,经常是这些范围限定了不能用哪些方法来解题。
其次我慢慢觉得代码风格也要注意,首先要尽可能精简准确,能一句话表达清楚的绝不用三五行,不要写得太过冗长,但同时不容易理解的地方也要写注释。写注释不仅可以给别人看的时候会好些,更重要的是可以帮助自己理清思路,知道自己每一步要做什么。精简和注释并不矛盾。
相关文章推荐
- 中南大学第十一届大学生程序设计竞赛网络预选赛总结
- 中南大学第十一届大学生程序设计竞赛-COJ1903-Tricky数
- 中南大学第十一届大学生程序设计竞赛-COJ1898-复盘拉火车
- 中南大学第十一届大学生程序设计竞赛-COJ1895-Apache is late again
- 中南大学第十一届大学生程序设计竞赛-COJ1899-Yuelu Scenes
- 中南大学第十一届大学生程序设计竞赛-COJ1897-The price table of the snack store
- 中南大学第十一届大学生程序设计竞赛-COJ1896-Symmetry
- 中南大学第十一届大学生程序设计竞赛-COJ1901-赏赐 OR 灾难
- CCPC第十一届东北地区大学生程序设计竞赛(2017) 总结
- 中南大学第十一届大学生程序设计竞赛-COJ1900-锋芒不露
- 2015关于第十一届"蓝狐网络杯"湖南省大学生计算机程序设计竞赛的总结
- 中南大学第十一届大学生程序设计竞赛-COJ1904-精灵的交际网
- 中南大学第十一届大学生程序设计竞赛-COJ1902-Happy Chinese Poker
- 组队赛#2解题总结 (BNU 第十一届北京师范大学程序设计竞赛)
- 湖南省第十一届大学生程序设计竞赛:Internet of Lights and Switches(HASH+二分+异或前缀和)
- 外卖的撕‘哔’大战 Contest2074 - 中南大学第九届大学生程序设计竞赛网络预选赛
- 河南省第七届ACM大学生程序设计竞赛总结
- 第十一届“蓝狐网络杯”湖南省大学生计算机程序设计竞赛
- 中南大学第七届大学生程序设计竞赛
- 河南省第八届ACM大学生程序设计竞赛总结