最稳妥的方法是把数据copy出来写个搜索,当然也可以尝试直接在纸上找答案,有风险。
另外还有一种想法,因为一眼看上去这个答案就很接近500,先找到一组接近500的情况,然后类似找增广路一样看能否增大解。
这不就是tmd26进制么,除26取余。
写个递推,又快。除非你使用python,否则要对每项%10000.
直接枚举。
BFS。处理字典序仅需在建立方向数组时按照上右左下顺序即可。
好水
//
// Created by Visors on 2020/10/15.
//
// 题目名:TODO
// 题目来源:TODO
// 题目链接:TODO
// 算法:TeBieShuDeHe.cpp
// 用途:TODO
// 时间复杂度:O(TODO)
//
#include
using namespace std;
bool check(int x) {
int tmp;
while (x) {
tmp = x % 10;
if (tmp == 2 || tmp == 0 || tmp == 1 || tmp == 9) return true;
x /= 10;
}
return false;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int n;
long long ans = 0;
cin >> n;
for (int i = 1; i <= n; i++)
if (check(i)) ans += i;
cout << ans << endl;
return 0;
}
显然,第 k k k层的节点编号范围为 2 k − 1 ∼ 2 k − 1 2^{k-1}\sim 2^k-1 2k−1∼2k−1,依次我们直接 O ( n ) O(n) O(n)求出每层权值和并打擂。
当然,也可以转换一下想法,每层 2 k − 1 2^{k-1} 2k−1个节点,直接按每层节点读。
//
// Created by Visors on 2020/10/15.
//
// 题目名:TODO
// 题目来源:TODO
// 题目链接:TODO
// 算法:WanQuanErChaShuDeQuanZhi.cpp
// 用途:TODO
// 时间复杂度:O(TODO)
//
#include
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int n;
cin >> n;
int level = log2(n) + 1;
int ans = -0x3f3f3f3f, pos;
for (int i = 1; i <= level; i++) {
int tot = pow(2, i - 1);
int sum = 0;
if (i == level)
tot = n - (int) pow(2, level - 1) + 1;
for (int j = 1, t; j <= tot; j++) {
cin >> t;
sum += t;
}
if (sum > ans) {
ans = sum;
pos = i;
}
}
cout << pos << endl;
return 0;
}
我们将数列从小到大排序,显然在等差数列中对于任意两对连续两数之差,都不可能互素,所以最小的差值即为使等差数列最短的公差。
注意当公差为0时,如果采用除法计算将会除法除 0 0 0错误,此时需特判长度为 n n n.
//
// Created by Visors on 2020/10/15.
//
// 题目名:TODO
// 题目来源:TODO
// 题目链接:TODO
// 算法:DengChaShuLie.cpp
// 用途:TODO
// 时间复杂度:O(TODO)
//
#include
using namespace std;
vector<int> v;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int n;
cin >> n;
v.resize(n);
for (int &it:v) cin >> it;
int delta = 0x7fffffff;
sort(v.begin(), v.end());
for (int i = 1; i < v.size(); i++)
delta = min(delta, v[i] - v[i - 1]);
if (delta == 0) cout << n << endl;
else cout << (v.back() - v[0]) / delta + 1 << endl;
return 0;
}
这跟后缀表达式有什么关系,又不用输出最终表达式……
一开始想简单了,脑补了一棵树:
1 ∼ 8 1\sim 8 1∼8代表输入的数从大到小编号后的数。整颗表达式树化简后最终为 1 + 2 + 3 + 4 + 5 + 6 + 7 − 8 1+2+3+4+5+6+7-8 1+2+3+4+5+6+7−8,也就是说减去了个最小的数。看上去很对,但事实上这只对非负数成立,上面这种贪心方式有问题。所以我莽的一发只拿了45分,不出意外这45分数据都是只含非负数的(错误方法水45???)。
不过上面这个方法也同时给了我们启发,上面贪心错误的原因是我们可能加了负数。不过仔细想一想,这里面有一个很显然的性质:如果在SUB节点下,我们把负数安排在右孩子位置,则这个操作相当于PLUS这个负数的绝对值。
同样,如果是SUB节点后跟一系列操作符,展开表达式后这些操作符都会变号。
所以我们很容易能想到新的贪心——在有负号的情况下,我们可以将任意数量的PLUS变为SUB,同时因为SUB的存在左右置换优化,所以实际上我们可以把情况统一为PLUS绝对值。
当负号个数为0时,整个解的情况显然是全部的和;当负号个数大于0时,除了唯一一个无法消去的SUB外,其它所有的操作符和数都可以用上面的方法调整为PLUS绝对值。那么唯一的负号和肯定有的正号应该给哪些数呢?当然是SUB最小的那个数,PLUS最大的那个数。如果讲整个数列排序,则最优表达式的值为 ∑ i = 2 n − 1 ∣ v [ i ] ∣ − v [ 0 ] + v [ n ] \sum\limits_{i=2}^{n-1}|v[i]|-v[0]+v[n] i=2∑n−1∣v[i]∣−v[0]+v[n].
这题值得总结一下,如果现场直接莽可能就翻了。
//
// Created by Visors on 2020/10/15.
//
// 题目名:TODO
// 题目来源:TODO
// 题目链接:TODO
// 算法:HouZhuiBiaoDaShi.cpp
// 用途:TODO
// 时间复杂度:O(TODO)
//
#include
using namespace std;
vector<int> v;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int n, m;
cin >> n >> m;
v.resize(n + m + 1);
long long ans = 0;
for (int &it:v) cin >> it;
if (m == 0) {
for (int &it:v) ans += it;
} else {
sort(v.begin(), v.end());
ans = -v[0] + v.back();
for (int i = 1; i < v.size() - 1; i++) ans += abs(v[i]);
}
cout << ans << endl;
return 0;
}
这题有点难。
首先题目给出了一个连续三个数的操作,注意到这个操作不会使三个数的和发生变化,由是我们想到观察其前缀和。假设原数为 ( a i − 1 , a i , a i + 1 ) (a_{i-1},a_i,a_{i+1}) (ai−1,ai,ai+1),则操作后数为 ( a i − 1 + a i , − a i , a i + 1 + a i ) (a_{i-1}+a_i,-a_i,a_{i+1}+a_i) (ai−1+ai,−ai,ai+1+ai)。原前缀和为 ( S i − 1 , S i , S i + 1 ) (S_{i-1},S_i,S_{i+1}) (Si−1,Si,Si+1),操作后为 ( S i − 1 + a i , S i + a i − 2 a i , S i + 1 + a i − 2 a i + a i ) (S_{i-1}+a_i,S_i+a_i-2a_i,S_{i+1}+a_i-2a_i+a_i) (Si−1+ai,Si+ai−2ai,Si+1+ai−2ai+ai),整理后即为 ( S i , S i − 1 , S i + 1 ) (S_i,S_{i-1},S_{i+1}) (Si,Si−1,Si+1)。
我们发现这样操作三个数其实只是将它们前两个数的前缀和交换,那么我们不妨将这一三元操作转换为二元操作看待。转换的同时我们将求解目标也转换一下,即原来要求 max ( ∣ a i ∣ ) \max(|a_i|) max(∣ai∣),现在要求 max ( ∣ S i − S i − 1 ∣ ) \max(|S_i-S_{i-1}|) max(∣Si−Si−1∣)。那么如何取得该值呢?其实贪心就可以,这个描述起来有点复杂,可以先参考下面的代码,后面有时间我再详细补充。
//
// Created by Visors on 2020/10/16.
//
// 题目名:TODO
// 题目来源:TODO
// 题目链接:TODO
// 算法:LingNengChuanShu.cpp
// 用途:TODO
// 时间复杂度:O(TODO)
//
#include
using namespace std;
typedef long long ll;
int T, n;
vector<ll> v, preSum, s;
vector<bool> book;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> T;
while (T--) {
cin >> n;
v.resize(n);
preSum.resize(n + 1);
s.resize(n + 1);
book.resize(n + 1);
fill(book.begin(), book.end(), false);
for (ll &it:v) cin >> it;
preSum[0] = 0;
for (int i = 1; i <= n; i++)
preSum[i] = preSum[i - 1] + v[i - 1];
ll s0 = preSum[0], sn = preSum[n];
if (s0 > sn) swap(s0, sn);
sort(preSum.begin(), preSum.end());
for (int i = 0; i <= n; i++)
if (preSum[i] == s0) {
s0 = i;
break;
}
for (int i = n; i >= 0; i--)
if (preSum[i] == sn) {
sn = i;
break;
}
ll ans = -0x7fffffffffffffff;
int l = 0, r = n;
for (int i = s0; i >= 0; i -= 2) {
s[l++] = preSum[i];
book[i] = true;
}
for (int i = sn; i <= n; i += 2) {
s[r--] = preSum[i];
book[i] = true;
}
for (int i = 0; i <= n; i++)
if (!book[i])
s[l++] = preSum[i];
for (int i = 1; i <= n; i++) ans = max(ans, abs(s[i] - s[i - 1]));
cout << ans << endl;
}
return 0;
}