CF698D (计算几何+模拟)

题目大意:

k k k个射击点,平面上有 n n n只怪兽,每个射击点只能射击一次,且只能射击方向上第一个没死的怪兽。射击顺序任意,有多少只怪物可能被打死。

解题思路:

  • 整了个特别不好实现的思路…最后看的题解
  • 枚举每只怪兽 x x x,对于每只怪兽, k ! k! k!枚举开枪顺序,模拟过程大概是:第一枪 p 1 p_1 p1要射中 x x x,倘若中间有怪兽 y y y,那么第二枪 p 2 p_2 p2就要去射击 y y y,如果 p 2 p_2 p2 y y y之间有怪兽 z z z在中间,则继续递归下去即可
  • 预处理设计点和每个怪兽中间的怪兽,时间复杂度为 O ( k n 2 ) O(kn^2) O(kn2)
  • 枚举怪兽能否被杀的时间复杂度为: O ( k ! n k ) O(k!nk) O(k!nk)
  • 总时间复杂度: O ( k n 2 + k ! n k ) O(kn^2+k!nk) O(kn2+k!nk)

AC代码:

#include <bits/stdc++.h>
#define ft first
#define sd second
#define pb push_back
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) //不能跟puts混用
#define seteps(N) fixed << setprecision(N)
#define endl "\n"
const int maxn = 1e3 + 10;
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
int sgn(ll x) {
    if (x == 0) return 0;
    else return x < 0 ? -1 : 1;
}
struct Point {
    ll x, y;
    int id;
    Point operator - (Point p) {return {x - p.x, y - p.y};}
    Point operator + (Point p) {return {x + p.x, y + p.y};}
    ll operator ^ (Point p) {return x * p.y - y * p.x;}
    ll operator * (Point p) {return x * p.x + y * p.y;}
    ll len2() {return x * x + y * y;}
    ll dis2(Point p) {return (x - p.x) * (x - p.x) + (y - p.y) * (y - p.y);}
    bool operator < (Point p) {return len2() < p.len2();}
    bool operator == (Point p) {
        if (x != p.x || y != p.y) return false;
        else return true;
    }
} sto[10], mon[maxn];
vector <int> id[10][maxn];
int k, n, rk[10];
bool pd(Point p1, Point p2, Point p3) {
    int s = sgn((p3 - p1) ^ (p2 - p1));
    if (s) return false; //在同一条直线
    s = sgn((p3 - p1) * (p2 - p1));
    if (s < 0) return false; //不是同一个方向
    s = sgn(p1.dis2(p2) - p1.dis2(p3));
    if (s <= 0) return false; //距离不大于
    return true;
}
void init() {
    for (int i = 1; i <= k; i++)
        for (int j = 1; j <= n; j++)
            for (int l = 1; l <= n; l++) 
                if (pd(sto[i], mon[j], mon[l])) {
                    id[i][j].pb(l);
                    if (id[i][j].size() == k) break;
                }
}
queue <int> pq; 
bool vis[maxn];
int num;
bool dfs(int x) {
    if (num > k) return 0;
    int s = rk[num];
    for (int y : id[s][x]) {
        if (!vis[y]) {
            ++num;
            if (!dfs(y)) return 0;
        }
    }
    return pq.push(x), vis[x] = 1;
}
void solve() {
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= k; j++) rk[j] = j;
        do {
            while (!pq.empty()) vis[pq.front()] = 0, pq.pop();
            num = 1;
            if (dfs(i)) {
                ++ans;
                break;
            }
        } while (next_permutation(rk + 1, rk + k + 1));
    }
    cout << ans << endl;
}
int main() {
    cin >> k >> n;
    for (int i = 1; i <= k; i++) cin >> sto[i].x >> sto[i].y;
    for (int i = 1; i <= n; i++) cin >> mon[i].x >> mon[i].y;
    init();
    solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值