河北HBCPC2020 B连杀 (河北省大学生程序设计竞赛)

题目介绍

题目解读

这道题有两个地方需要注意

1.自己的坐标不是固定的

2.子弹射出后可以贯穿轨迹上的所有人

于是乎 题意就是:试找出最少的直线,能连接所有的点

洛谷有一道题和此题同类型题,不过直线方程改为了曲线方程

题目链接

解题思路

首先,两个点可以确定一条线

我们可以依次枚举每个点,此时有三种情况

1.前面已经构成的线已经把这个点给覆盖了

2.从被存起来的点中,选择一个点,构成一条新的线

3.把这个点存起来,暂时不做处理

由于平行x、y轴的直线需要特殊存储,所以需要存储三种直线

具体思路在代码中

AC代码

#include <bits/stdc++.h>

using namespace std;
const int maxn = 15;
int n;
double X[maxn + 1], Y[maxn + 1]; //敌人坐标
double tx[maxn + 1], ty[maxn + 1]; //存起来的敌人坐标
double K[maxn + 1], B[maxn + 1]; //直线方程 y=kx+b的 k和b
double k0[maxn + 1], k1[maxn + 1]; //k0:存储斜率为0的直线的y k1存储斜率为无穷大的直线的x
int ans = 10000;

bool isZero(double x) { //判断是否为0
    if (fabs(x) < 1e-6) {
        return true;
    }
    return false;
}

void dfs(int id, int u, int u0, int u1, int v) {
    //id:当前敌人, u:直线方程的个数, u0:斜率为0的直线的个数,
    // u1:斜率为无穷大的直线的个数, v:存起来的敌人的个数
    if (u + u0 + u1 + v >= ans) { //已经比答案大
        return;
    }
    if (id > n) {  //更新答案
        ans = u + u0 + u1 + v;
        return;
    }
    bool flag = false;
    //判断是否被前面的直线方程的连到过
    for (int i = 1; i <= u; i++) {
        if (isZero(Y[id] - K[i] * X[id] - B[i])) {
            //被前面的线连到过
            dfs(id + 1, u, u0, u1, v);
            flag = true;
            break;
        }
    }
    //判断是否被前面的斜率为0的直线的连到过
    if (!flag) {
        for (int i = 1; i <= u0; i++) {
            if (Y[id] == k0[i]) {
                dfs(id + 1, u, u0, u1, v);
                flag = true;
                break;
            }
        }
    }
    //判断是否被前面的斜率为无穷大的直线的连到过
    if (!flag) {
        for (int i = 1; i <= u1; i++) {
            if (X[id] == k1[i]) {
                dfs(id + 1, u, u0, u1, v);
                flag = true;
                break;
            }
        }
    }
    //没有被连过
    if (!flag) {
        //跟被存起来的人连线
        for (int i = 1; i <= v; i++) {
            if (isZero(Y[id]- ty[i])) { //能构成斜率为0的线
                k0[u0 + 1] = Y[id]; //别忘了把这个直线存起来
                double q = tx[i], w = ty[i];
                for (int j = i; j < v; j++) { //把这个被存起来的敌人移出来
                    tx[j] = tx[j + 1];
                    ty[j] = ty[j + 1];
                }
                dfs(id + 1, u, u0 + 1, u1, v - 1);
                for (int j = v; j > i; j--) { //回溯被存起来的敌人
                    tx[j] = tx[j - 1];
                    ty[j] = ty[j - 1];
                }
                tx[i] = q;
                ty[i] = w;
            }
            if (isZero(X[id]-tx[i])) { //能构成斜率为0的线
                k1[u1 + 1] = X[id]; //存起来
                double q = tx[i], w = ty[i];
                for (int j = i; j < v; j++) {
                    tx[j] = tx[j + 1];
                    ty[j] = ty[j + 1];
                }
                dfs(id + 1, u, u0, u1 + 1, v - 1);
                for (int j = v; j > i; j--) {
                    tx[j] = tx[j - 1];
                    ty[j] = ty[j - 1];
                }
                tx[i] = q;
                ty[i] = w;
            }
            if (!isZero(Y[id] - ty[i]) && !isZero(X[id] - tx[i])) { //能构成直线方程
                double k = (Y[id] - ty[i]) / (X[id] - tx[i]);
                double b = Y[id] - k * X[id];
                K[u + 1] = k;
                B[u + 1] = b;
                double q = tx[i], w = ty[i];
                for (int j = i; j < v; j++) {
                    tx[j] = tx[j + 1];
                    ty[j] = ty[j + 1];
                }
                dfs(id + 1, u + 1, u0, u1, v - 1);
                for (int j = v; j > i; j--) {
                    tx[j] = tx[j - 1];
                    ty[j] = ty[j - 1];
                }
                tx[i] = q;
                ty[i] = w;
            }
        }
        //选择存起来
        tx[v + 1] = X[id];
        ty[v + 1] = Y[id];
        dfs(id + 1, u, u0, u1, v + 1);
    }
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%lf%lf", &X[i], &Y[i]);
    }
    dfs(1, 0, 0, 0, 0);
    printf("%d", ans);
}

  • 19
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值