【算法积累】Catch That Cow & 轻重搭配(C++ & Java)

A - Catch That Cow

Farmer John has been informed of the location of a fugitive cow and wants to catch her immediately. He starts at a point N ( 0 ≤ N ≤ 100 , 000 ) N (0≤N≤100,000) N(0N100,000) on a number line and the cow is at a point K ( 0 ≤ K ≤ 100 , 000 ) K (0≤K≤100,000) K(0K100,000) on the same number line. Farmer John has two modes of transportation: walking and teleporting.

  • Walking: FJ can move from any point X X X to the points X − 1 X - 1 X1 or X + 1 X + 1 X+1 in a single minute
  • Teleporting: FJ can move from any point X X X to the point 2 × X 2 × X 2×X in a single minute.

If the cow, unaware of its pursuit, does not move at all, how long does it take for Farmer John to retrieve it?

Input
Line 1: Two space-separated integers: N N N and K K K

Output
Line 1: The least amount of time, in minutes, it takes for Farmer John to catch the fugitive cow.

Sample Input

5 17

Sample Output

4

Hint
The fastest way for Farmer John to reach the fugitive cow is to move along the following path: 5 − 10 − 9 − 18 − 17 5-10-9-18-17 51091817, which takes 4 4 4 minutes.

题目分析
最短路径问题,很容易想到广度优先搜索 (BFS)。因为在BFS中,我们需要用到队列 (queue) 这种数据结构,我们也知道队列的特性是FIFO(first in first out,先进先出),正是这个特性保证了我们第一个到达目标点的路径一定是最短路径。
关于BFS的更为详细的解释可以参考我的上一篇博客:【算法积累】Oil Deposits(DFS&BFS)

代码
语言:C++

#include <iostream>
#include <queue>
#define MAXSIZE 100005
using namespace std;

int step[MAXSIZE];
bool visited[MAXSIZE];

int bfs(int start, int end) {
    queue<int> q;
    q.push(start);
    visited[start] = true;
    while (!q.empty()) {
        int x = q.front();
        q.pop();
        for (int i = 0; i < 3; i++) {
            int tmp;
            switch (i) {
                case 0:
                    tmp = x + 1;
                    break;
                case 1:
                    tmp = x - 1;
                    break;
                case 2:
                    tmp = x * 2;
                    break;
            }
            if (tmp < 0 || tmp > 100000 || visited[tmp]) {
                continue;
            }
            q.push(tmp);
            visited[tmp] = true;
            step[tmp] = step[x] + 1;
            if (tmp == end) {
                return step[end];
            }
        }
    }
    return -1;
}

int main() {
    int n, k;
    cin >> n >> k;
    cout << (n >= k ? n - k : bfs(n, k)) << endl;
    return 0;
}

语言:Java

import java.util.LinkedList;
import java.util.Scanner;
import java.util.Queue;

public class Main {
    public static int[] step = new int[100005];
    public static boolean[] visited = new boolean[100005];

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int N = input.nextInt();
        int K = input.nextInt();
        System.out.println(N >= K ? (N - K) : bfs(N, K));
        input.close();
    }

    public static int bfs(int start, int end) {
        Queue<Integer> q = new LinkedList<Integer>();
        q.add(start);
        visited[start] = true;
        while (!q.isEmpty()) {
            int x = q.element();
            q.remove();
            for (int i = 0; i < 3; i++) {
                int next = 0;
                switch (i) {
                    case 0:
                        next = x + 1;
                        break;
                    case 1:
                        next = x - 1;
                        break;
                    case 2:
                        next = x * 2;
                        break;
                }
                if (next < 0 || next > 100000 || visited[next]) {
                    continue;
                }
                q.add(next);
                visited[next] = true;
                step[next] = step[x] + 1;
                if (next == end) {
                    return step[end];
                }
            }
        }
        return -1;
    }
}

B - 轻重搭配

n n n个同学去动物园参观,原本每人都需要买一张门票,但售票处推出了一个优惠活动,一个体重为 x x x的人可以和体重至少为 2 x 2x 2x配对,这样两人只需买一张票。现在给出了 n n n个人的体重,请你计算他们最少需要买几张门票?

输入格式
第一行一个整数 n n n,表示人数。
第二行 n n n个整数,每个整数 a i a_i ai表示每个人的体重。

输出格式
一个整数,表示最少需要购买的门票数目。

数据范围
对于 30 30% 30的数据: 1 ≤ n ≤ 25 , 1 ≤ a i ≤ 100 1≤n≤25,1≤a_i≤100 1n25,1ai100
对于 60 60% 60的数据: 1 ≤ n ≤ 10000 , 1 ≤ a i ≤ 1000 1≤n≤10000,1≤a_i≤1000 1n10000,1ai1000
对于 100 100% 100的数据: 1 ≤ n ≤ 5 ⋅ 1 0 5 , 1 ≤ a i ≤ 1 0 5 1≤n≤5⋅10^5,1≤a_i≤10^5 1n5105,1ai105

样例解释
1 1 1 9 9 9配对, 7 7 7 3 3 3配对,剩下 5 , 5 5,5 5,5单独,一共买四张票。

Sample Input

6
1 9 7 3 5 5

Sample Output

4

题目分析
不难想到我们最多可以组成 n / 2 n/2 n/2对,因此可以采用二分的策略,分为前后两组进行查找配对。

代码
语言:C++

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

int a[MAXSIZE];
bool flag[MAXSIZE];

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    sort(a + 1, a + n + 1);
    int mid = n / 2;
    int high = n;
    int ans = 0;
    while (mid) {
        if (a[mid] * 2 <= a[high]) {
            ans++;
            flag[mid] = true;
            flag[high] = true;
            mid--;
            high--;
        } else {
            mid--;
        }
    }
    for (int i = 1; i <= n; i++) {
        if (!flag[i]) {
            ans++;
        }
    }
    cout << ans << endl;
    return 0;
}

语言:Java

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static int[] a = new int[500005];
    public static boolean[] flag = new boolean[500005];

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        for (int i = 1; i <= n; i++) {
            a[i] = input.nextInt();
        }
        Arrays.sort(a, 1, n + 1);
        int mid = n / 2;
        int high = n;
        int ans = 0;
        while (mid != 0) {
            if (a[mid] * 2 <= a[high]) {
                ans++;
                flag[mid] = true;
                flag[high] = true;
                mid--;
                high--;
            } else {
                mid--;
            }
        }
        for (int i = 1; i <= n; i++) {
            if (!flag[i]) {
                ans++;
            }
        }
        System.out.println(ans);
        input.close();
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值