2016 年宁波工程学院第七届ACM校赛题解报告
本题解代码直接为比赛代码,仅供参考。
A,B,C,D,G,H,J,K,L,M 来自 Ticsmtc 同学.
F 来自 Gealo 同学.
E,I 来自Alex 学长.
Promblem A : Two Sum
时间限制: 1 Sec 内存限制: 64 MB
题目描述:
给出n个数,另外给出⼀个整数S,判断是否可以从中取出2个数,使得这两个数的和是S。
输入:
第⼀行有个整数T(1 <= T <= 10),代表数据组数。 对于每组数据,第⼀行包含两个整数n,S(2 <= n <= 103, 1 <= S <= 1000) 接下来⼀行包含n个整数,整数的⼤小在1到1000。
输出:
对于每组数据,如果存在2个整数,和为S,则输出Yes,否则输出No。
样例输入:
2
5 6
1 2 3 4 5
5 10
1 2 3 4 5
样例输出:
Yes
No
题解:
题目意思不难理解,我们控制两重循环枚举一下是哪两个数,然后用加法判断下是不是S,如果是就可以,如果枚举完了所有的都没有发现能等于S的,那么就是不行。
这里注意下,n个数,我们从下标零读入所有的数的话,那么外层循环i从0 ~ n- 1,考虑内层的时候,由于两个数不能够选重,所以内层循环 j 应当从 i + 1 ~ n - 1 ,然后看存储数字的save数组,save[i] + save[j] 是否等于 S 。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <stack> 4 #include <cstring> 5 #include <algorithm> 6 #include <string> 7 using namespace std; 8 9 int save[10000]; 10 int main(){ 11 int t; 12 cin >> t; 13 while(t--){ 14 int n; 15 int S; 16 cin >> n; 17 cin >> S; 18 for(int i = 0 ; i < n ; i++){ 19 cin >> save[i]; 20 } 21 /// 22 bool ok = 0; 23 for(int i = 0 ; i < n - 1 ; i++){ 24 for(int j = i + 1 ; j < n ; j++){ 25 if(save[i] + save[j] == S){ 26 ok = 1; break; 27 } 28 } 29 } 30 //// 31 if(ok) cout << "Yes" << endl; 32 else cout << "No" << endl; 33 34 } 35 return 0; 36 }
Problem B : Poker
时间限制: 1 Sec 内存限制: 128 MB
题目描述:
很多牌中都有顺子,今天⼩小编给您5张牌,需要客官您判断下这5张牌能不能连成⼀把同花顺。
同花顺即花⾊一致的顺子,注意最⼩的顺子是A 2 3 4 5,最⼤的是10 J Q K A。
输入:
第⼀行有⼀个整数T(1 <= T <= 30),代表数据组数。 每组数据包含5行,每⼀行包含牌的⾯值和花色
⾯值是{2 3 4 5 6 7 8 9 10 J Q K A}当中的一个, 花⾊是S(spade)、H(heart)、C(club)、D(diamond)其中之⼀。
输出:
如果给定的牌可以组成同花顺,输出Yes;否则输出No。
样例输入:
2
5 H
6 H
7 H
8 H
9 H
5 H
6 H
7 S
8 C
9 D
样例输出:
Yes
No
题解:
先解决读入问题,很多同学尝试用两个char 读入前后一个牌号一个花色,那么当牌号是10的时候,char 只读入一个字符,就会出现错误,其次,如果用scanf 方式读取字符,需要很小心的考虑空格和回车符,这些都会被当作字符读入。所以十分麻烦。
我采取的做法是读取字符串,然后转化回数字。
好的,接下来我们用到一个手牌数组,save[] , 假如花色没有不同的,save数组中存储所有牌面的数字。
这题的数字中的 A 比较特别, 既可以当作 1 看待 , 也可以当作 14 看待, 那么很有意思的事是当我们碰到A的时候转化成几。我选择了特判 A , 相当于把 1 和 14 都放进了手牌。
我们对手牌save数组排序,从小到大,然后判断一下连续的五张牌是不是都是递增且相差一,如果有A的话,要判断两遍,因为有A,相当于手牌数组中有六个数,可能由1 ~ 5 个数(A为1)是一个顺子,也可能由2 ~ 6 个数(A为14)是一个顺子。
然后就A掉了呐~。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <stack> 4 #include <cstring> 5 #include <algorithm> 6 #include <string> 7 using namespace std; 8 9 int save[100010]; 10 11 int trans(string &in){ 12 if(in.size() == 2){ 13 return 10; 14 } 15 char k = in[0]; 16 if(k >= '1' && k <= '9') return k - '0'; 17 if(k == 'J') return 11; 18 if(k == 'Q') return 12; 19 if(k == 'K') return 13; 20 } 21 22 23 int main(){ 24 int t; 25 cin >> t; 26 while(t--){ 27 char color = ' '; 28 bool ok = 1; 29 bool is_A = 0; 30 int p = 0; 31 for(int i = 0 ; i < 5 ; i++){ 32 //cout << i << endl; 33 string temp; char c; 34 cin >> temp >> c; 35 //cout << temp << " " << c << endl; 36 if(color == ' ') color = c; 37 else if(color != c){ 38 ok = 0; 39 } 40 /// 41 if(temp == "A"){ 42 save[p++] = 1; 43 save[p++] = 14; 44 is_A = 1; 45 } 46 else save[p++] = trans(temp); 47 } 48 if(!ok) {printf("No "); continue;} 49 // 50 sort(save,save + p); 51 // 52 int pre = save[0]; 53 for(int i = 1 ; i <= 4 ; i++){ 54 if(save[i] != pre + 1){ 55 ok = 0; break; 56 } 57 pre = save[i]; 58 } 59 //cout << "is A " << is_A << endl; 60 if(is_A && !ok){ 61 ok = 1; 62 pre = save[1]; 63 for(int i = 2 ; i <= 5 ; i++){ 64 if(save[i] != pre + 1){ 65 ok = 0; break; 66 } 67 pre = save[i]; 68 } 69 //cout << "ok " << ok << endl; 70 } 71 //// 72 if(!ok) printf("No "); 73 else printf("Yes "); 74 } 75 return 0; 76 }
Problem C: I Need Minmum Number
时间限制: 1 Sec 内存限制: 64 MB
题目描述:
你有⼀个序列,一开始是空的,接下来执行n次操作,每次操作都是以下3种之⼀
2 value 表示在序列末尾加⼊一个新的数
1 表⽰删除序列末尾的那个数(此次操作在序列为空时不会出现)
0 表⽰示询问当前序列⾥的最小值(此次操作在序列为空时不会出现)
输入:
第⼀行有个整数T(1 <= T <= 10),代表数据组数。 对于每组数据,第⼀行包含⼀个整数n(1 <= n <= 105) 接下来包含n⾏,代表n次操作。 如果是一个整数0,代表询问当前序列⾥的最小值。 如果是一个整数1,代表删除序列末尾的那个数。
否则就是2 value,代表在序列末尾加入value(-10000 <= value <= 10000)。
输出:
对于每个0操作,输出当前序列里的最小值。
样例输入:
1
5
2 3
2 1
0
2 -1
0
样例输出:
1
-1
题解:
想用一个栈,和一个变量存储最小值,插入一个数就比较一下,询问的时候直接输出这个存储最小值变量,这样的同学太天真了。
问题是,还有弹出的操作,万一栈顶是最小值,你弹出了栈顶,然后你记录最小值的这个东西又要更新(你从哪里更新呢)。。。。
很多种做法,我的做法是设置两个数组,save数组模拟栈操作,minlist数组也是一个栈,纪录save栈中连续不增的子序列。那么得到以下规则:
1.插入一个数val时,val插入save栈末尾。如果val比minlist栈顶元素小或等于栈顶元素,那么将val插入minlist栈,如果val比minlist栈顶元素大,则不操作。说明save栈在插入val前还有比val小的数。
2.删除save栈顶的数时,首先删除这个栈顶数,其次比较下,这个栈顶数val,是不是等于minlist栈顶数,如果相等,同样将minlist栈顶删除。如果不等于,那么不做操作。
3.请求save栈中最小元素时,即为minlist的栈顶元素。
然后就A掉了呐~。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <stack> 4 #include <cstring> 5 using namespace std; 6 7 int save[100010]; 8 int minlist[100010]; 9 10 int main(){ 11 int t; 12 scanf("%d",&t); 13 while(t--){ 14 int k; 15 scanf("%d",&k); 16 // 17 int pos = 0; 18 int minpos = 0; 19 // 20 for(int i = 0 ; i < k ; i++){ 21 int op; 22 scanf("%d",&op); 23 if(op == 2){ 24 int val; 25 scanf("%d",&val); 26 save[pos++] = val; 27 /// 28 if(minpos == 0){ 29 minlist[minpos++] = val; 30 } 31 else if(val <= minlist[minpos - 1]){ 32 minlist[minpos++] = val; 33 } 34 } 35 else if(op == 1){ 36 int temp = save[pos - 1]; 37 pos--; 38 //////// 39 if(temp == minlist[minpos - 1]){ 40 minpos--; 41 } 42 } 43 else{ 44 printf("%d ",minlist[minpos - 1]); 45 } 46 } 47 } 48 return 0; 49 }
Problem D : Two’s Complement
时间限制: 1 Sec 内存限制: 64 MB
题目描述:
现代大部分计算机都采用补码的形式存储有符号数,一个长度为w的补码b,
对应的十进制数字为:
其中bi要么为1,要么为0。
例如,采用5位来表示时,-1的补码就是11111,现在告诉你一串补码,求出对应
的十进制下的数字。
输入:
第一行有一个整数T,代表数据组数(T <= 1000)。
每组数据输入只有一行,代表补码(长度小于等于30,大于等于1)。
输出:
对于每组数据,输出该补码对应的十进制数。
样例输入:
3
1001
10
001
样例输出:
-7
-2
1
题解:
嗯,其实这题需要知道二进制表示的一些知识,一个二进制数,我们从右到左叫做从低位到高位,那么我们从低位到高位的权值依次从2^0 , 2^1 , 2^2 …..
比如,二进制数 101
如何得到十进制数呢 , 从低位到高位,三位的权值分别为 2^0 , 2^1 , 2^2.
那么他的十进制就是1 * 2^0 + 0 * 2^1 + 1 * 2^2.
好了,明白了低位高位和权值的概念,这题就已经可以做了。
这个数学公式的意思是:
-1 * 最高位上的数字 * 最高位上的权值 +(除去最高位后,次高位到最低位表示的这个二进制数。)
比如11111(5个1)是-1,是如何得到的呢?
首先我们发现最高位(也就是最左位)是1,权值是2^4,那么根据公式前一部分 , 计算出为-16.
除去了最高位剩下了1111(四个1)按照二进制计算为15。
那么两部分加起来就是-1啦。
读进来的时候用字符串,然后自己操作下权值就好?
A掉了哇~
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <stack> 4 #include <cstring> 5 using namespace std; 6 char save[100010]; 7 int main(){ 8 int t; 9 scanf("%d",&t); 10 while(t--){ 11 scanf("%s",save); 12 int len = strlen(save); 13 int ans = 0; 14 long long quan = 1; 15 for(int i = len - 1; i >= 1 ; i--){ 16 ans += (quan * (save[i] - '0')); 17 quan*=2; 18 } 19 ans = ans + (-1) * (save[0] - '0') * quan; 20 cout << ans << endl; 21 } 22 return 0; 23 }
Problem E : Just Go
时间限制: 3 Sec 内存限制: 64 MB
题目描述:
There is a river, which contains n stones from left to right. These stones are magic, each
one has a magic number Ai which means if you stand on the ith stone, you can jump to (i +1)th stone, (i+2)th stone, ..., (i+Ai)th stone(when i+Ai > n, you can only reach as far as n), you want to calculate the number of ways to reach the nth stone.
Notice: you can not jump from right to left!
输入:
Input starts with an integer T(1 <= T <= 10), denoting the number of test cases. Each test case contains an integer n(1 <= n <= 105), denoting the number stones. Next line contains n integers Ai(1 <= Ai <= 108).
输出:
For each test case, print the number of way to reach the nth stone module 109+7.
样例输入:
3
5
1 2 3 4 5
1
10
2
2 1
样例输出:
3
1
1
题解:
按照题目意思,模拟操作,套数据结构线段树。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 #include <algorithm> 3 #include <math.h> 4 using namespace std; 5 6 const int N = 110010; 7 const int mod = 1e9 + 7; 8 9 struct SegTree { 10 int l, r; 11 int way; 12 int add; 13 }tree[N << 2]; 14 15 void modify(int &target, int val) { 16 target += val; 17 target = (target % mod + mod) % mod; 18 } 19 20 void pushup(int p) { 21 modify(tree[p].way, (tree[p << 1].way + tree[p << 1 | 1].way) % mod); 22 } 23 24 void build(int p, int l, int r) { 25 tree[p].l = l; 26 tree[p].r = r; 27 tree[p].add = tree[p].way = 0; 28 if (l == r) { 29 if (l == 1) { 30 tree[p].way = 1; 31 } 32 return; 33 } 34 int mid = (l + r) >> 1; 35 build(p << 1, l, mid); 36 build(p << 1 | 1, mid + 1, r); 37 pushup(p); 38 } 39 40 void pushdown(int p) { 41 if (tree[p].add) { 42 modify(tree[p << 1].add, tree[p].add); 43 modify(tree[p << 1 | 1].add, tree[p].add); 44 modify(tree[p << 1].way, tree[p].add); 45 modify(tree[p << 1 | 1].way, tree[p].add); 46 tree[p].add = 0; 47 } 48 } 49 50 void update(int p, int l, int r, int val) { 51 if (l <= tree[p].l && tree[p].r <= r) { 52 modify(tree[p].add, val); 53 modify(tree[p].way, val); 54 return; 55 } 56 pushdown(p); 57 int mid = (tree[p].l + tree[p].r) >> 1; 58 if (r <= mid) { 59 update(p << 1, l, r, val); 60 } 61 else if (l > mid) { 62 update(p << 1 | 1, l, r, val); 63 } 64 else { 65 update(p << 1, l, mid, val); 66 update(p << 1 | 1, mid + 1, r, val); 67 } 68 pushup(p); 69 } 70 71 int query(int p, int pos) { 72 if (tree[p].l == tree[p].r) { 73 return tree[p].way; 74 } 75 pushdown(p); 76 int mid = (tree[p].l + tree[p].r) >> 1; 77 if (pos <= mid) { 78 return query(p << 1, pos); 79 } 80 else { 81 return query(p << 1 | 1, pos); 82 } 83 } 84 85 int main() { 86 int t; 87 scanf("%d", &t); 88 while (t--) { 89 int n; 90 scanf("%d", &n); 91 int val; 92 build(1, 1, n); 93 for (int i = 1; i <= n; ++i) { 94 scanf("%d", &val); 95 int way = query(1, i); 96 int s = i + 1; 97 int e = min(n, i + val); 98 if (i == n) { 99 continue; 100 } 101 update(1, s, e, way); 102 } 103 printf("%d ", query(1, n)); 104 } 105 return 0; 106 }
Problem F : Fat Brother’s new way
时间限制: 1 Sec 内存限制: 64 MB
题目描述:
I bet, except Fat Brothers, all of you don’t like strange way to show integers , he is really like this way to showing integers:
1 -> ‘A’
2 -> ‘B’
…….
26 -> ‘Z’
27 -> ‘AA’
28 -> ‘AB’
…….
Unfortunately, Fat Brother’s mathematics is poor, so he needs your help, he will give you some integers, and you must transform it with Fat Brother’s way.
输入:
Input starts with an integer T(T <= 10000), denoting the number of test case.
For each test case, an integers n(1 <= n <= 2147483647) is given.
输出:
For each case, output the corresponding string with upper-case letters.
样例输入:
3
17311
2068
37
样例输出:
YOU
CAN
AK
题解:
先将26个字母存入0~25的数组中,第一步将n - 1对26取余,余数便是对应的字母所在的位置,接下来便是将n除以26直到0为止反复进行这一系列的操作。还有一些细节值得注意。
当n为52时,答案是AZ但只是按这种方法输出会是BZ由于52/26=2。所以在每次n%26=0时n/26
时要减一
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 char a[27] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 6 char b[100000]; 7 int main() 8 { 9 int t; 10 scanf("%d" , &t); 11 int n; 12 for(int i = 0 ; i < t ; i++) 13 { 14 scanf("%d" , &n); 15 int g = 0; 16 while(n > 0) 17 { 18 if(n % 26 != 0) 19 b[g++] = a[n % 26 - 1]; 20 else 21 { 22 b[g++] = a[25]; 23 n--; 24 } 25 if(n == 26) 26 break; 27 n /= 26; 28 } 29 for(int i = g - 1 ; i >= 0 ; i--) 30 printf("%c" , b[i]); 31 printf(" "); 32 } 33 return 0; 34 }
Problem G : Is The Same?
时间限制: 1 Sec 内存限制: 64 MB
题目描述:
给出2个字符串S和T,如果可以通过循环移位使得S和T相等,则我们称S和T是同构字符串, 例如S=“abcd”, T=“bcda”,则S和T是同构字符串;而S=“abcd”和T=“bcad”则不是同构字符串。
循环移位是指:在⼀个长度为n的字符串S中,取⼀个任意下标i,把字符串分为两段,分别为 S1S2...Si 和Si+1Si+2...Sn,然后把字符串变为Si+1Si+2...SnS1S2...Si,例如S=“qwerty”,取i=3, 则变 为”rtyqwe”(注意,一个字符串本⾝身也算是它的同构字符串)。
输入:
第⼀行包含一个整数T(1 <= T <= 20),代表测试组数。
对于每组数据,包含2个字符串,字符串长度都小于等于105且非空,输入保证字符串只包含小写字⺟。
输出:
对于每组数据,如果这两个字符串是同构字符串,则输出Yes,否则输出No。
样例输入:
2
abcd
bcda
abcd
bcad
样例输出:
Yes
No
题解:
姑且算有点算法的水题吧,我们把第一个串叫做A串,第二个叫做B串,如果A串长度和B串都不一样,那么肯定不同构。
那么A串和B串长度相等时,我们继续。
好像有很多的思路,我的想法是将A串倍增两倍,比如A串是abcde,我倍增后A串为abcdeabcde,然后在倍增后的A串中找一下有没有B串就行了。找的到就是同构,找不到就不同构。
字符串匹配,套个KMP就稳稳的过掉了~
A掉了呐~
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <stack> 4 #include <cstring> 5 using namespace std; 6 char A[300010]; 7 char B[300010]; 8 int Next[300010]; 9 10 void check(){ 11 int len = strlen(B); 12 for(int i = 0; i < len ; i++){ 13 cout << Next[i] << " "; 14 } 15 cout << endl; 16 return ; 17 } 18 19 void make_next(){ 20 Next[0] = -1; 21 int len = strlen(B); 22 int i = 0, k = -1; 23 while(i < len){ 24 if(k == -1 || B[i] == B[k]){ 25 Next[++i] = ++k; 26 } 27 else k = Next[k]; 28 } 29 return ; 30 } 31 32 bool kmp(){ 33 make_next(); 34 int len1 = strlen(A); 35 int len2 = strlen(B); 36 //// 37 int i = 0 , k = 0; 38 while(i < len1 && k < len2){ 39 if(k == -1 || A[i] == B[k]){i++; k++;} 40 else k = Next[k]; 41 } 42 if(k == len2) return 1; 43 else return 0; 44 } 45 46 int main(){ 47 int t; 48 scanf("%d",&t); 49 while(t--){ 50 scanf("%s",A); 51 scanf("%s",B); 52 int len = strlen(A); 53 // 54 if(len != strlen(B)) {printf("No "); continue;} 55 // 56 for(int i = 0 ; i < len ; i++){ 57 A[len + i] = A[i]; 58 } 59 A[2 * len - 1] = '