题目链接
不妨先解决这个问题的判断非降序的部分。既然符合条件的数组移除一个数字就能使得整个数组非降序,那么我们先统计满足 a[i−1]>a[i] 的i的个数cnt。另外,令idx等于当 cnt=1 时的满足 a[i−1]>a[i] i的值。
下面根据cnt的值来分类讨论:
- 当 cnt=0 时数组满足条件。
- 当 cnt>1 时数组不满足条件。
- 当 cnt=1 时, idx=1 或 idx=n−1 的话满足条件(删去数组的某端就能满足条件),删去a[idx-1]或a[idx]能够使得整个数组非降序的话也满足条件,否则就不满足条件。
对非升序的判断只要把数组颠倒一下顺序,然后重复非降序的判断方法即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int t, n, a[maxn];
bool isIncrease() {
int idx, cnt = 0;
for(int i = 1; i < n; i++) {
if(a[i-1] > a[i]) {
idx = i;
cnt++;
}
}
if(cnt == 0) return true;
if(cnt > 1) return false;
if(idx == 1 || idx == n - 1) {
return true;
}
if(a[idx-1] <= a[idx+1]) {
return true;
}
if(a[idx-2] <= a[idx]) {
return true;
}
return false;
}
bool isDecrease() {
reverse(a, a + n);
return isIncrease();
}
int main() {
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
if(isIncrease() || isDecrease()) {
puts("YES");
}
else puts("NO");
}
return 0;
}
本题需要判断若干个点是否正好构成一个正多边形。刚开始想得很复杂,觉得应该先求个凸包来判断是否这些点收是否正好构成一个凸多边形。后来才发现,这些点构成正多边形只须满足它们的重心到各个点的距离相等就好了。
#include <bits/stdc++.h>
using namespace std;
struct Point {
double x, y;
Point(double x = 0, double y = 0): x(x), y(y) {}
double distance(Point p) {
return hypot(x - p.x, y - p.y);
}
};
const double eps = 1e-10;
const int maxn = 105;
int t, n;
double x, y, sx, sy;
Point p[maxn];
int dcmp(double x) {
if(fabs(x) < eps) return 0;
return x < 0 ? -1 : 1;
}
int main() {
scanf("%d", &t);
for(; t--;) {
scanf("%d", &n);
sx = sy = 0;
for(int i = 0; i < n; i++) {
scanf("%lf%lf", &x, &y);
p[i] = Point(x, y);
sx += x;
sy += y;
}
bool flag = true;
Point c = Point(sx / n, sy / n);
double dis = c.distance(p[0]);
for(int i = 1; i < n; i++) {
if(dcmp(c.distance(p[i]) - dis)) {
flag = false;
break;
}
}
puts(flag ? "YES" : "NO");
}
return 0;
}
由于满足连通性和 n=m−1 (其中为 n 点数, m 为边数)的图就是树,所以在为 n 个结点分配 2(n−1) 个度数且保证每个节点都有非零度数后,定能构造出一棵合法的树。若没有“每个结点都有非零度数”这一限制的话,这就是一个完全背包的问题。其中总度数为背包的体积,度数i为物品的体积, f(i) 为物品的价值。为了解决这一限制给我们带来的麻烦,我们假设先给每个结点分配了一个度数,这下就可以进行完全背包了。背包的体积为 n−1 ,度数i为物品的体积,价值为 f(i)−f(1) 。为什么价值改变了呢?因为往背包里拿一个度数为 i 代表的物品就相当于让一个度数为 1 的结点的度数增加到 i 。由于体积小于节点数,因此不会发生改变已经改变过的结点的事情。也就是说,可以保证每次改变点的度数(往背包中拿一件物品)都是对度数为 1 的点进行的。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2020;
int t, n, m, f[maxn], d[maxn];
int main() {
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
m = n - 2;
for(int i = 1; i < n; i++) {
scanf("%d", &f[i]);
}
memset(d, 0, sizeof(d));
d[0] = n * f[1];
for(int i = 2; i < n; i++) {
for(int j = i - 1; j <= m; j++) {
d[j] = max(d[j], d[j-(i-1)] + f[i] - f[1]);
}
}
printf("%d\n", d[m]);
}
return 0;
}
求集合中两个数的最大异或值,这样的问题非常适合用 Trie来高效的求解。先将所有的数的二进制表示的字符串插入Trie。然后枚举两个数i, j,并将这两个数删除,接着直接在Trie中找与i + j的异或值最大的那个数,并更新最大值。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10, maxNode = 1e7;
int t, n, a[maxn], ans;
struct trie {
int sz, val[maxNode], ch[maxNode][2];
void init() {
memset(ch[0], 0, sizeof(ch[0]));
sz = 1;
}
void update(int num, int v) {
int u = 0;
for(int i = 30; i >= 0; i--) {
int c = (num >> i) & 1;
if(ch[u][c] == 0) {
memset(ch[sz], 0, sizeof(ch[sz]));
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
val[u] += v;
}
}
int match(int num) {
int u = 0, ans = 0;
for(int i = 30; i >= 0; i--) {
int c = (num >> i) & 1;
if(ch[u][c^1] && val[ch[u][c^1]]) {
ans |= (1 << i);
u = ch[u][c^1];
}
else u = ch[u][c];
}
return ans;
}
}o;
int main() {
scanf("%d", &t);
for(; t--;) {
scanf("%d", &n);
o.init();
for(int i = 0; i < n; i++) {
scanf("%d", &a[i]);
o.update(a[i], 1);
}
ans = 0;
for(int i = 0; i < n; i++) {
o.update(a[i], -1);
for(int j = i + 1; j < n; j++) {
o.update(a[j], -1);
int tmp = o.match(a[i] + a[j]);
ans = max(ans, tmp);
o.update(a[j], 1);
}
o.update(a[i], 1);
}
printf("%d\n", ans);
}
return 0;
}
我们可以枚举柱体(即矩阵中的元素)。对每个柱体,计算它的高度与四个方向上的柱体的高度差d,另外考虑柱顶也要贴玻璃,所以计算出该柱体对答案的贡献为d + 1。注意,由于每组的输入的矩阵规模可能不同,所以进入每组的计算前都要对二维数组进行初始化(或者显式地对边界情况进行判断)。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 55;
// 用于表示四个方向坐标变化情况的二维数组
const int dir[2][4] = {{-1, 1, 0, 0}, {0, 0, -1, 1}};
int t, x, y, n, m, d, sum, c[maxn][maxn];
int main() {
scanf("%d", &t);
for(; t--;) {
sum = 0;
scanf("%d%d", &n, &m);
memset(c, 0, sizeof(c));
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
scanf("%d", &c[i][j]);
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(c[i][j]) sum++;
else continue;
// 枚举四个方向
for(int k = 0; k < 4; k++) {
x = i + dir[0][k];
y = j + dir[1][k];
d = c[i][j] - c[x][y];
// 正的落差才能被加到结果中
if(d > 0) sum += d;
}
}
}
printf("%d\n", sum);
}
return 0;
}
(其他题目略)