[Offer收割]编程练习赛26

小Hi与法阵

题解

按照条件求解出最大的三角形和最小的三角形,然后求重心即可。
这里求解面积有两种方法:

  1. 海伦公式: p(pa)(pb)(pc) ,将三角形的每一条边求解出来,然后进行处理
  2. 用有向向量进行计算,如果设 a(x0,y0) , b(x1,y1) , c(x2,y2) 三点为三角形三个顶点, A 为有向面积,那么
    2A=x0x1x2y0y1y2111=x0y1+x2y0+x1y2x2y1x0y2x1y0

然后需要注意就是针对浮点数判断要进行误差处理 eps=108 就可以了。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#include <cmath>
using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
const int MAXN = 50 + 5;
const int MAXM = 5e6 + 5;
const int INF = 0x3f3f3f3f;
const double eps = 1e-10;
int N, U;
struct Point {
    double x, y;
} P[MAXN];
int A[2][3];
double area(double x0, double y0, double x1, double y1, double x2, double y2) {
    return fabs(x0 * y1 + x1 * y2 + x2 * y0 - x2 * y1 - y2 * x0 - y0 * x1) / 2.;
}

pair<double, double> getZx(int i, int j, int k) {
    return make_pair((P[i].x + P[j].x + P[k].x) / 3., (P[i].y + P[j].y + P[k].y) / 3.);
}
int main() {
    int _;
    scanf("%d", &_);
    while(_ --) {
        scanf("%d%d", &N, &U);
        for(int i = 0; i < N; i ++) {
            scanf("%lf%lf", &P[i].x, &P[i].y);
        }
        memset(A, 0, sizeof(A));
        double max_area = -1;
        double min_area = (LL)INF * INF * 2.;
        for(int i = 0; i < N; i ++) {
            for(int j = i + 1; j < N; j ++) {
                for(int k = j + 1; k < N; k ++) {
                    double cur_area = area(P[i].x, P[i].y, P[j].x, P[j].y, P[k].x, P[k].y);
                    if(cur_area <= eps) continue;
                    pair<double, double> tmp1 = getZx(i, j, k);
                    pair<double, double> tmp2 = getZx(A[0][0], A[0][1], A[0][2]);
                    pair<double, double> tmp3 = getZx(A[1][0], A[1][1], A[1][2]);
                    if(cur_area - max_area > eps ||
                            fabs(cur_area - max_area) <= eps && (tmp1.first - tmp2.first > eps ||
                                    fabs(tmp1.first - tmp2.first) <= eps && tmp1.second - tmp2.second > eps)) {
                        max_area = cur_area;
                        A[0][0] = i, A[0][1] = j, A[0][2] = k;
                    }
                    if(min_area - cur_area > eps ||
                            fabs(min_area - cur_area) <= eps && (tmp3.first - tmp1.first > eps ||
                                    fabs(tmp3.first - tmp1.first) <= eps && tmp3.second - tmp1.second > eps)) {
                        min_area = cur_area;
                        A[1][0] = i, A[1][1] = j, A[1][2] = k;
                    }
                }
            }
        }
        pair<double, double> a = getZx(A[0][0], A[0][1], A[0][2]);
        pair<double, double> b = getZx(A[1][0], A[1][1], A[1][2]);
        double ret = (a.second - b.second) * (a.second - b.second) + (a.first - b.first) * (a.first - b.first);
        printf("%.2f\n", sqrt(ret) / (2. * U));

    }
}

小Hi与花盆

题解

两种思维方式

  1. 直接处理,也就是说用一个map或者set来存储每一次的操作,比如说我向00000000插入5,变成了0000X000也就是说把8变成了43,如此用一个map存起来,如果当前有一个值等于K就输出结果即可
  2. 用转正为负的方法,我们开始是不断往空的盆子里放花,现在变成了不断从盆子里拿花,我们将处理数据离线处理,然后从N-1次操作往前处理,很明显0会不断增多,这样就有点类似于并查集了,我们合并连在一起的0,当这个合并的值等于K的时候是不是就是答案,当然我们必须找到最接近开始的操作

代码(map,set)

#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <unordered_map>
#include <map>
#include <set>
using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
const int MAXN = 1e5 + 5;
const int MAXM = 5e6 + 5;
const int INF = 0x3f3f3f3f;
const double eps = 1e-10;
int N, K;
set<int> A;
int main() {
    while(~scanf("%d%d", &N, &K)) {
        A.clear();
        A.insert(0);
        A.insert(N + 1);
        int t = -2, x;
        for(int i = 0; i < N; i ++) {
            scanf("%d", &x);
            if(t != -2) continue;
            set<int>::iterator it = A.upper_bound(x);
            int r = *it --;
            int l = *it;
            if(K == x - l - 1 || K == r - x - 1) {
                t = i;
            }
            A.insert(x);
        }
        printf("%d\n", t + 1);
    }
    return 0;
}

代码(并查集)

#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <unordered_map>
#include <map>
#include <set>
using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
const int MAXN = 2e5 + 5;
const int MAXM = 5e6 + 5;
const int INF = 0x3f3f3f3f;
const double eps = 1e-10;
int N, K;
int par[MAXN];
int cnt[MAXN];
int A[MAXN];
void init(){
    for(int i = 0;i < MAXN;i ++) par[i] = i, cnt[i] = 0;
}
int _find(int x){
    return x == par[x] ? x : par[x] = _find(par[x]);
}

int unit(int a, int b){
    a = _find(a);
    b = _find(b);
    if(a == b) return cnt[a];
    par[a] = b;
    cnt[b] += cnt[a];
    return cnt[b];
}
int main() {
    while(~scanf("%d%d", &N, &K)) {
        init();
        for(int i = 0;i < N;i ++) scanf("%d", &A[i]);
        int ret = -1;
        int kt = 0;
        for(int i = N - 1;i >= 0;i --){
            cnt[A[i]] = 1;
            int tmp = 1;
            if(A[i] - 1 >= 1 && cnt[A[i] - 1]){
                int t = cnt[_find(A[i] - 1)];
                tmp = unit(A[i], A[i] - 1);
                if(t == K) kt --;
            }
            if(A[i] + 1 <= N && cnt[A[i] + 1]){

                int t = cnt[_find(A[i] + 1)];
                tmp = unit(A[i], A[i] + 1);
                if(t == K) kt --;
            }
            if(tmp == K) kt ++;
            if(kt > 0) ret = i;
            //if(kt != -1) ret = kt;
        }
        printf("%d\n", ret);
    }
    return 0;
}

小Hi与矩阵

题解

将菱形转换为正方形,经典的针对求解曼哈顿距离的方法

代码

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

const int MAXN = 2e2 + 5;
int S[20 + 5][MAXN << 1][MAXN << 1];
int A[MAXN << 1][MAXN << 1];
int N;
int sum(int v, int x0, int y0, int x1, int y1) {
    x0 --;
    y0 --;
    if(x0 < 0) x0 = 0;
    if(y0 < 0) y0 = 0;
    if(x1 >= N * 2) x1 = N * 2;
    if(y1 >= N * 2) y1 = N * 2;
    return S[v][x1][y1] - S[v][x1][y0] - S[v][x0][y1] + S[v][x0][y0];
}

int main() {
    int x;
    while(~scanf("%d", &N)) {
        memset(A, 0, sizeof(A));
        memset(S, 0, sizeof(S));
        for(int i = 1; i <= N; i ++) {
            for(int j = 1; j <= N; j ++) {
                scanf("%d", &x);
                A[i + j - 1][N - i + j] = x;
            }
        }
        int max_v = 0;
        for(int i = 1; i <= 20; i ++) {
            for(int j = 1; j <= N * 2; j ++) {
                for(int k = 1; k <= N * 2; k ++) {
                    S[i][j][k] = S[i][j - 1][k] + S[i][j][k - 1] - S[i][j - 1][k - 1] + (i == A[j][k]);
                }
            }
        }
        int Q;
        scanf("%d", &Q);
        int y, k;
        while(Q --) {
            scanf("%d%d%d", &x, &y, &k);
            int i = x + y - 1;
            int j = N - x + y;
            int ret = 0;
            //printf("%d,%d,%d\n", i, j, k);
            for(int f = 1; f <= 20; f ++) {
                if(k % f == 0)ret += sum(f, i - k, j - k, i + k, j + k);
            }
            printf("%d\n", ret);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值