【算法训练营】最近点对,纸牌,青蛙(Python实现)

最近点对


描述

给定n个二维平面上的点,求距离最近的一对点,输出他们的距离。

输入

第一行包含一个正整数n。

接下来n行,每行包含两个整数x,y,表示一个点的坐标。

输出

输出距离最近的一对点的距离,保留两位小数。

样例1输入

10
7 9
-8 -1
-3 -1
1 4
-3 9
6 -4
7 5
6 6
-6 10
0 8

样例1输出

1.41

样例1解释

距离最近的点为7和8,距离为√(7−6)2+(5−6)2=√2≈1.41(7-6)2+(5-6)2=2≈1.41

样例2输入

点击下载

限制

对于70%的数据,2 ≤ n ≤ 2000,每个点坐标的绝对值不超过10^5;

对于100%的数据,2 ≤ n ≤ 3×10^5,每个点坐标的绝对值不超过10^9。

时间:10 sec

空间:512 MB

提示

[分治求最近点对。当然也可以用kdtree,虽然应该会超时。]

代码实现 

import sys
from math import sqrt


def distance(point1, point2):
    return sqrt((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2)


def closest_pair(points, l, r):
    if l == r:
        return sys.float_info.max
    if r - l == 1:
        return distance(points[l], points[r])

    mid = (l + r) // 2
    dl = closest_pair(points, l, mid)
    dr = closest_pair(points, mid + 1, r)
    min_d = min(dl, dr)

    mid_points = []
    for i in range(l, r + 1):
        if abs(points[i][0] - points[mid][0]) < min_d:
            mid_points.append(points[i])

    mid_points.sort(key=lambda point: point[1])

    for i in range(len(mid_points)):
        for j in range(i + 1, len(mid_points)):
            if mid_points[j][1] - mid_points[i][1] >= min_d:
                break
            min_d = min(min_d, distance(mid_points[i], mid_points[j]))

    return min_d


def main():
    n = int(input())
    points = [(0, 0)] * n
    for i in range(n):
        points[i] = tuple(map(int, input().split()))

    points.sort(key=lambda point: point[0])

    ans = closest_pair(points, 0, n - 1)
    print(f"{ans:.2f}")


if __name__ == "__main__":
    main()

纸牌


时间限制:1 sec

空间限制:512 MB

问题描述

小明有 2n 张纸牌,点数依次从1 到 2n。小明要和你玩一个游戏,这个游戏中,每个人都会分到 n 张卡牌。游戏一共分为 n 轮,每轮你们都要出一张牌,点数小者获胜。

游戏开始了,你拿到了你的牌。你现在想知道,你最多(也就是运气最好的情况下)能够获胜几轮?

输入格式

第一行 1 个正整数 n。

第 2 行到第 n+1 行每行一个正整数 a[i],表示你的第 i 张牌的点数。

输出格式

一行一个整数表示你最多能够获胜的轮数。

样例输入

2
1
4

样例输出

1

数据范围

对于 31.25% 的数据,保证 1<=n<=100

对于 100% 的数据,保证 1<=n<=50,000

保证数据的合法性,即你即不会拿到重复的牌,又不会拿到超出点数范围的牌。

代码实现 

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    int x, cnt = 0;
    scanf("%d", &n);
    int N = 2 * n + 1;
    int a[N], b[N];
    memset(a, 0, sizeof(a));
    memset(b, 0, sizeof(b));

    for (int i = 1; i <= n; ++i) {
        scanf("%d", &x);
        a[x] = x;
    }

    for (int j = 1; j <= 2 * n; j++)
        if (a[j] == 0) {
            b[j] = j;
        }

    int index1 = 1, index2 = 1;
    for (; index1 <= 2 * n, index2 <= 2 * n;) {
        for (; a[index1] == 0 && index1 < 2 * n; ++index1);
        for (; b[index2] == 0 && index2 < 2 * n; ++index2);
        if (a[index1] < b[index2]) {
            ++cnt;
            ++index1;
            ++index2;
        } else {
            ++index2;
        }
    }

    printf("%d", cnt);
    return 0;
}

青蛙


题目名称:小青蛙

时间限制:5 sec

空间限制:256 MB

问题描述

一个坐标轴上有 n个荷叶,编号从 11 到 n。每片荷叶有一个坐标。

有一只可爱的小青蛙,它任选一片荷叶作为起点,并选择一个方向(左或右)然后开始跳。第一次跳跃时,他没有任何限制。从第二次跳跃开始,受到魔法的影响,他每次跳跃的距离都必须不小于前一次跳跃的距离,且跳跃方向必须与上一次跳跃保持一致。

每一片荷叶上都有一个数值。每次小青蛙跳到一片荷叶上时,他就会获得该荷叶对应的数值。特别地,他初始选择的荷叶的数值也是能得到的。

小青蛙可以在任意时刻选择停止跳跃。

可爱的小青蛙希望能获得尽可能大的数值总和。你能帮帮她吗?

输入格式

输出格式

一行一个整数,表示小青蛙能够获得的最大的数值总和。

样例输入

6
5 6
1 1
10 5
7 6
4 8
8 10

样例输出

25

数据范围

提示

本题时间限制较大,可以考虑一些效率一般的算法哦!

代码实现 

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int MAX_LEAVES = 1005;
pair<int, int> leaves_coord[MAX_LEAVES];
int n;
int dp[MAX_LEAVES][MAX_LEAVES];

int main() {
    cin >> n;
    
    // Read the coordinates of the leaves on the axis
    for (int i = 1; i <= n; ++i) {
        int x, s;
        cin >> x >> s;
        leaves_coord[i] = make_pair(x, s);
    }
    
    int answer = 0;
    for (int round = 0; round < 2; ++round) {
        sort(leaves_coord + 1, leaves_coord + n + 1);
        for (int i = 1; i <= n; ++i) {
            dp[i][i] = leaves_coord[i].second;
            for (int j = 1; j < i; ++j) {
                dp[i][j] = 0;
                for (int k = j; k && 2 * leaves_coord[j].first <= leaves_coord[i].first + leaves_coord[k].first; --k)
                    dp[i][j] = max(dp[i][j], dp[j][k]);
                answer = max(answer, (dp[i][j] += leaves_coord[i].second));
            }
        }
        
        // Reflect the leaves along the axis to continue using the sorting algorithm
        for (int i = 1; i <= n; ++i)
            leaves_coord[i].first = -leaves_coord[i].first;
    }
    
    cout << answer << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

X.AI666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值