A. Bad Triangle
题意:
给一组非降序列(a),问这组序列是否存在三个数使得这三个数无法构成三角形。
思路:
因为序列是排好序的,根据三角形任意两边之和大于第三边,判断第(1,2,n)条边能否构成三角形就好了。
代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
#define INF 1000000005
typedef long long ll;
const int maxn = 50005;
using namespace std;
int a[maxn], T, n;
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", a + i);
if (a[0] + a[1] <= a[n - 1]) {
printf("1 2 %d
", n);
}
else if (a[n - 1] - a[0] >= a[1]) {
printf("1 2 %d
", n);
}
else {
printf("-1
");
}
}
return 0;
}
B. Substring Removal Game
题意:
给定一组(01)串,每次可以选择一段连续的(0)串或者(1)串。(Alice)和(Bob)在进行这场游戏,(Alice)先手。每个人都有分数,分数等于这个人选过所有数字的和。(Alice)和(Bob)都希望自己的分数尽可能高。问(Alice)的分数最高是多少?
思路:
两个玩家每次都一定选择最长的一串(1)。因为如果有一个玩家选择了一串(0),那么另一个玩家就能一次选择更多的(1),犯不着给对手方便。如果一个玩家一次只选择一串(1)的一段,那么这一段剩下的(1)就有可能在将来归对手所有,不如一次直接全选。
代码:
#include <cstdio>
#include <cstring>
#include <map>
#include <queue>
const int maxn = 128;
using namespace std;
char s[maxn];
int n, T, cur, cnt, ans;
priority_queue<int> q;
inline void init() {
cur = 0;
ans = 0;
cnt = 0;
while (!q.empty()) q.pop();
}
int main() {
scanf("%d", &T);
while (T--) {
init();
scanf("%s", s);
n = strlen(s);
while (cur < n) {
while (s[cur] == '0' && cur < n) cur++;
cnt = 0;
while (s[cur] == '1' && cur < n) {
cnt++;
cur++;
}
q.push(cnt);
}
bool flag = false;
while (!q.empty()) {
int u = q.top();
q.pop();
if (!flag)
ans += u;
flag = !flag;
}
printf("%d
", ans);
}
return 0;
}
C. Good Subarrays
比赛的时候没想出来,所以是补题的时候补的。(其实比赛的时候想得差不多了QAQ,没特别考虑(sum_i - i = 0)的情况,所以代码不对QAQ)
题意:
给定(n)和一串(n)个数字,如果存在一段连续的(sumlimits_{i=l}^{r}a_i=r - l + 1),那么我们称这一段子串为good subarray。问一共有多少good subarrays。
思路:
先做一遍前缀和,得到所有的(sum_i)。这样,我们要求的就变成了(sum_r - sum_{l - 1} = r - l + 1)。
移项,得(sum_r - r = sum_{l-1} - (l - 1))
所以,我们求出所有的(sum_i - i),然后(ans = ans + num_{sum_i - i}, num_{sum_i - i} = num_{sum_i - i} + 1)。
其中,(num_{sum_i - i})是之前(sum_i - i)出现的次数。
这里要特别注意一下(sum_i - i = 0)的情况,是因为,(sum_i - i = 0)第一次出现的时候,从(1)到(i)求和本身就是一个good subarray。所以要把(num_0)初始化为(1)。
代码:
代码中的(sum)是直接利用(a)数组存的。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
typedef long long ll;
using namespace std;
const int maxn = 100005;
int T, n;
ll a[maxn];
ll ans = 0;
map<ll, ll> p;
inline void read(ll *a) {
int len = 0;
char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') {
a[++len] = ch - '0';
a[len] += a[len - 1];
ans += p[a[len] - len];
p[a[len] - len]++;
ch = getchar();
}
}
inline void init() {
p.clear();
a[0] = 0;
ans = 0;
}
int main() {
scanf("%d", &T);
while (T--) {
init();
scanf("%d", &n);
p[0] = 1;
read(a);
printf("%lld
", ans);
}
return 0;
}
D. Colored Rectangles
题意:
你有(RGB)三种颜色的木棍。告诉你每种颜色的木棍有几对。问你用这些木棍凑出来所有矩形面积之和最大是多少。凑矩形必须满足如下条件:
- 对边颜色相同
- 邻边颜色不同
- 一条边只能用一根木棍
思路:
用木棍凑面积我们很容易想到一种贪心策略,就是每次取最长的两组木棍凑成矩形。所以我们对每一组木棍长度由大到小排序后跑DP。用(f_{i,j,k})表示使用了(i)根红木棍,(j)根绿木棍,(k)根蓝木棍凑出来所有矩形的最大面积之和。那么,我们有如下状态转移方程:
代码:
#include <cstdio>
#include <algorithm>
#include <map>
#include <queue>
typedef long long ll;
using namespace std;
ll color[3], f[205][205][205];
ll q[3][205];
bool cmp(const ll &a, const ll &b) {
return a > b;
}
int main() {
for (int i = 0; i < 3; i++)
scanf("%lld", color + i);
for (int i = 0; i < 3; i++) {
for (int j = 1; j <= color[i]; j++) {
scanf("%lld", &q[i][j]);
}
}
for (int i = 0; i < 3; i++)
sort(q[i] + 1, q[i] + color[i] + 1, cmp);
ll ans = 0;
for (int i = 0; i <= color[0]; i++) {
for (int j = 0; j <= color[1]; j++) {
for (int k = 0; k <= color[2]; k++) {
if ((i + j + k) & 1) continue;
if (!(i + j + k)) continue;
if (i >= 1 && j >= 1) f[i][j][k] = max(f[i][j][k], f[i - 1][j - 1][k] + q[0][i] * q[1][j]);
if (i >= 1 && k >= 1) f[i][j][k] = max(f[i][j][k], f[i - 1][j][k - 1] + q[0][i] * q[2][k]);
if (j >= 1 && k >= 1) f[i][j][k] = max(f[i][j][k], f[i][j - 1][k - 1] + q[1][j] * q[2][k]);
ans = max(ans, f[i][j][k]);
}
}
}
printf("%lld
", ans);
return 0;
}