C题:题目给了一个整数数组a与字符串数组s,然后说s数组用来匹配a数组,即s[i]='>',那么a[i]必须大于0.而s有'>','<','Z'三种字符,分别代表三种意思.但现在的a数组并不匹配s数组,因此要我们改动a数组以满足题意,这就是我们要解决的.看样例那么就来到了本题的"诈骗点",按照题意,后一种的状态受前一种决定,那么应该是dp,实际上这也能dp来做,但这将把问题复杂化,我们只需要先遍历s的>,<字符,将对应的a修改一致,再对s的Z字符遍历,对不符合的再修改,这样既符合了前面的s,也符合了现在的s,也就是说前面的是<,那么我要把a对应的变为负数,后面的是Z,我只需要也改成负数就行了,否则将要多改一次,而且前面的<是不能动的.这就是贪心的思想.(当时没有考虑到Z字符的特殊性,只是遍历S修改次数,这应该是多数人一开始的想法,然后很多人wa了之后会考虑dp的方案,即思路错误了).
具体操作详见代码(记得开i64,有乘法):
using namespace std;
using i64 = long long;
void solve() {
int n;
cin >> n;
vector<i64>a(n);
string s;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
cin >> s;
i64 ans = 0;
for (int i = 0; i < n; i++) {
if (s[i] == '>') {
if (a[i] <= 0) {
a[i] = 1;
ans++;
}
} else if (s[i] == '<') {
if (a[i] >= 0) {
a[i] = -1;
ans++;
}
}
}
for (int i = 0; i < n; i++) {
if (s[i] == 'Z') {
if (a[i]*a[i - 1] <= 0) {
a[i] = a[i - 1];
ans++;
}
}
}
cout << ans << endl;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
D题:给出长度为n的数组,数组有正有负,要求使得数组总和最大.题目又给了两个操作,第一个操作选择两个数删去,第二个操作选择三个数删去.若想数组总和最大,应该通过操作尽可能地将负数删去.通过样例,我们可以通过操作将连续的负数删去,最后剩下若干个单个负数或者是没有负数,但很遗憾这正是出题人设下的诈骗点,诈骗我们去贪过去,事实是根本贪不了(不过作者想到了一个貌似能贪过去的solution,但代码及其丑且冗长,细看代码其实还是用了dp因此题解即最优解).这是一道很经典的线性dp,根据题目条件有三种决策:1.不删数字.2删两个数字.3删三个数字.分别代表状态转移方程:dp[i]=dp[i-2],dp[i]=dpi-3,然后on遍历数组取最优情况即可.代码如下:
using namespace std;
using i64 = long long;
void solve() {
int n;
cin >> n;
vector<i64> dp(n + 1);
dp[0] = 0;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
dp[i] += dp[i - 1] + x;
if (i >= 2) {
dp[i] = max(dp[i], dp[i - 2]);
}
if (i >= 3) {
dp[i] = max(dp[i], dp[i - 3]);
}
}
cout << dp[n] << endl;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}