[Nowcoder]2020牛客寒假算法基础集训营4
20200211 第四场
进度(4 / 10) 未完成:C / F / G / H / I / J
A、欧几里得
1、链接
https://ac.nowcoder.com/acm/contest/3005/A
2、题面
欧几里得算法是一种求最大公约数的有效算法,在算法竞赛中很常用。
def gcd(a,b): if b == 0: return a return gcd(b,a%b)
3、思路
打表找规律。然后就发现是个斐波那契数列了。
4、代码
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define MAXN 105 5 typedef long long ll; 6 7 ll a[MAXN], b[MAXN], mx, t; 8 9 int main() { 10 cin >> t; 11 for (int i = 1; i <= t; i++) cin >> a[i], mx = max(a[i], mx); 12 b[0] = 1, b[1] = 3, b[2] = 5; 13 for (int i = 3; i <= mx; i++) b[i] = b[i - 1] + b[i - 2]; 14 for (int i = 1; i <= t; i++) cout << b[a[i]] << endl; 15 return 0; 16 }
B、括号序列
1、链接
https://ac.nowcoder.com/acm/contest/3005/B
2、题面
注意:数据已加强并进行了rejudge
- 空串是一个合法的括号序列
- 如果A, B 都是合法的括号序列,那么AB也是合法的括号序列
- 如果A是合法的括号序列,(A) , [A], {A}都是合法的括号序列
3、思路
标准栈问题。
4、代码
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define MAXN 1000005 5 6 stack <int> s; 7 char ch[MAXN]; 8 int l; 9 10 int main() { 11 cin >> ch + 1, l = strlen(ch + 1); 12 for (int i = 1; i <= l; i++) { 13 if (ch[i] == '(' || ch[i] == '[' || ch[i] == '{') s.push(ch[i]); 14 else { 15 if (s.empty()) cout << "No", exit(0); 16 int o = s.top(); 17 if (ch[i] == ')' && o == '(' || ch[i] == ']' && o == '[' || ch[i] == '}' && o == '}') s.pop(); 18 else cout << "No", exit(0); 19 } 20 } 21 cout << (s.empty() ? "Yes" : "No"); 22 return 0; 23 }
D、子段异或
1、链接
https://ac.nowcoder.com/acm/contest/3005/D
2、题面
输入一个数列a,你需要输出其中异或值为0的不同子段的数量。一个子段 [l,r] (1≤l≤r≤n)的异或值为al⊕al+1⊕al+2⊕…⊕ar,其中⊕符号代表异或运算。
两个子段被视为相同的,当且仅当其开始和结束位置均对应相同。
3、思路
先求了一下前缀异或s[],发现当且仅当s[l - 1] = s[r],[l, r]的异或值为0,则记录下所有前缀异或值,用map记录每一个出现过的值和出现次数,如果出现次数为i,则可以得到(i - 1) * i / 2个满足条件的子段。
4、代码
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define MAXN 200005 5 typedef long long ll; 6 7 map <ll, ll> mp; 8 ll a[MAXN], b[MAXN], n, c[MAXN], o, ans; 9 10 int main() { 11 cin >> n; 12 for (int i = 1; i <= n; i++) cin >> a[i]; 13 b[1] = a[1], mp[0] = 0, c[0]++; 14 for (int i = 1; i <= n; i++) { 15 b[i] = i == 1 ? a[i] : b[i - 1] ^ a[i]; 16 if (!mp.count(b[i])) mp[b[i]] = ++o; 17 c[mp[b[i]]]++; 18 } 19 for (int i = 0; i <= o; i++) ans += c[i] * (c[i] - 1) / 2; 20 cout << ans; 21 return 0; 22 }
E、最小表达式
1、链接
https://ac.nowcoder.com/acm/contest/3005/E
2、题面
给出一个包含数字1-9和加号的字符串,请你将字符串中的字符任意排列,但每种字符数目不变,使得结果是一个合法的表达式,而且表达式的值最小。输出那个最小表达式的值
合法的表达式的定义如下:
- 一个数字,如233,是一个合法的表达式
- A + B是合法的表达式,当且仅当 A , B 都是合法的表达式
保证给出的表达式经过重排,存在一个合法的解。
3、思路
首先将数字和加号分离,由于使总和最小,所有尽可能使数平均。设加号的个数x,则分出的数的个数为x - 1。将数字从大到小排列,然后从个位开始依次将这些数字填入这x - 1个数,可证这样总和最小。
4、代码
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define MAXN 500005 5 6 int ans[MAXN], x = 1, l, hav; 7 char ch[MAXN]; 8 9 struct cmp { 10 bool operator () (char a, char b) { 11 return a > b; 12 } 13 } cmpx; 14 15 int main() { 16 cin >> ch + 1, l = strlen(ch + 1); 17 sort(ch + 1, ch + l + 1, cmpx); 18 while (ch[l] == '+') x++, l--; 19 for (int i = 0; i < l / x; i++) 20 for (int j = 1; j <= x; j++) { 21 ans[i] += ch[i * x + j] - '0'; 22 ans[i + 1] += ans[i] / 10, ans[i] %= 10; 23 } 24 if (l % x) 25 for (int i = 1; i <= l % x; i++) { 26 ans[l / x] += ch[l / x * x + i] - '0'; 27 ans[l / x + 1] += ans[l / x] / 10, ans[l / x] %= 10; 28 } 29 for (int i = l / x + 1; i >= 0; i--) 30 if (ans[i] || hav) cout << ans[i], hav = 1; 31 return 0; 32 }