CF993C 飞船大战

1 CF993C 飞船大战

2 题目描述

时间限制 \(2s\) | 空间限制 \(256M\)

有两艘小宇宙飞船被两组敌人的大宇宙飞船包围了。他们在一个二维平面上,有一组敌人的飞船的所有 \(x\) 坐标都是 \(-100\)\(y\) 坐标都是整数,另外一组所有 \(x\) 坐标都是 \(100\)\(y\) 坐标都是整数。两组飞船里面的每艘飞船都同时发出两束激光(功率极大,接触到的飞船都会被彻底摧毁)。一束瞄准一艘小飞船,另外一束瞄准另外一艘小飞船。两艘小飞船都可以躲开所有的激光射击,现在两艘小飞船想把把自己移动到横轴 \(x=0\) 的坐标位置,\(y\) 不限(\(y\) 不需要必须是整数),这样朝向他们发射的激光会摧毁敌人的飞船。假设敌人的大飞船不会躲避激光,找出这种方法可以摧毁的最多的飞船数量。

数据范围:\(1≤𝑛,𝑚≤60\),分别是\(x=-100\)\(x=100\) 飞船的个数;\(|\)𝑦\(_1\)\(_,\)\(_𝑖|≤10000\)\(|\)𝑦\(_2\)\(_,\)\(_𝑖|≤10000\) 分别是两组飞船的 \(y\) 坐标。

3 题解

因为我方飞船可能在非整数位置,且位置的数据范围很大,所以我们无法直接枚举我方飞船可能的位置。观察性质发现:如果位于 \((100, y_1)\) 的敌人的飞船击落了位于 \((-100, y_2)\) 的敌人的飞船,那么我们肯定有一架飞船位于 \((0, \dfrac{y_1 + y_2}{2})\) 这一位置。这一性质可以用全等证明。此时,我们可以用 \(map\) 帮助我们将所有可能的敌人的飞船位置标记出来并去重。那么我们是否可以直接枚举左右两侧的敌人的飞船,处理出每个我方位置可以击落敌人飞船的个数,然后输出最大值 \(+\) 次大值呢?

这里存在一个问题:对于两个不同的我方位置,可能存在某些敌人的飞船被判定攻击过了两次。如果我们直接去重,会导致时间复杂度过高,无法通过此题。这里我们想到:可以用一个二进制位表示一艘敌人的飞船。\(0\) 代表该敌人的飞船并未被击落,\(1\) 代表该敌人的飞船已经被击落。我们可以用两个 \(bitset\) 维护这些信息,分别代表左侧和右侧的敌人的飞船。最终我们只需要将枚举的两个不同敌人的飞船位置所对应的不同的 \(bitset\) 状态或起来,然后统计 \(1\) 的个数即可。

4 代码(空格警告):

#include <iostream>
#include <bitset>
#include <cstring>
#include <map>
using namespace std;
const int N = 65;
const int M = 1e4+10;
int n, m, cnt, ans;
int y_1[N], y_2[N];
bitset<65>l[N*N], r[N*N], c, d;
map<int,int> Map;
int main()
{ 
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> y_1[i];
    for (int i = 1; i <= m; i++) cin >> y_2[i];
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (!Map[y_1[i] + y_2[j]]) Map[y_1[i] + y_2[j]] = ++cnt;
            l[Map[y_1[i] + y_2[j]]].set(i);
            r[Map[y_1[i] + y_2[j]]].set(j);
        }
    }
    for (int i = 1; i <= cnt; i++)
    {
        for (int j = 1; j <= cnt; j++)
        {
            c = l[i] | l[j];
            d = r[i] | r[j];
            ans = max(ans, int(c.count() + d.count()));
        }
    }
    cout << ans;
    return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-03-15 21:58  David24  阅读(179)  评论(0编辑  收藏  举报