牛客泡泡堂题解
解法1:AC
由题意得知一定有一列会被炸死,那么我们枚举这是哪一列。紧接着我们需要快速得知在第列的时候哪一行能炸死最多的人?于是这个我们可以用线段树或者来维护。这里我选取的维护。维护在第列时每一行能炸死的人数,然后丢进里面,从set取最大的人数那么就知道答案了。需要注意的是我们需要在计算第列时每一行能炸死的人数时先把当前在第x列的人数去掉来防止重复计算。每次当我们枚举每一列时,我们只需要把列的人贡献从set去掉(因为炸弹炸不到了),同时还需要把列的人的贡献加入set,可以看出对于每一个元素都会被加入两次删除两次,所以时间复杂度,空间复杂度
set做法:
const int N = 200001; vector<int>xpos[N];//保存的是第x列的人的y的坐标 int x[N],y[N];//保存的是第x列的个数,第y行的贡献 set<pair<int,int>>s; class Solution { public: int BoomKill(int n, int m, vector<Point>& Player) { // write code here for(auto i:Player) { xpos[i.x].push_back(i.y); x[i.x]++; } for(int i=1;i<=min(n,m);i++) {//预处理在1列时每一行y的贡献 int cnt=0; for(auto j:xpos[i]) y[j]++; } for(int i=1;i<=n;i++) {//预处理在1列时每一行y的贡献 s.insert({y[i],i}); } for(auto j:xpos[1]) {//把第一列上的人的贡献删除 s.erase(s.lower_bound({y[j],j})); s.insert({--y[j],j}); } int ans=(*--s.end()).first+x[1];//第一列上答案的最大值 for(auto j:xpos[1]) {//把第一列上的人的贡献加上 s.erase(s.lower_bound({y[j],j})); s.insert({++y[j],j}); } for(int i=2;i<=n;i++) { if(i-m>=1) {//把i-m列的人的贡献删去 for(auto j:xpos[i-m]) { s.erase(s.lower_bound({y[j],j})); s.insert({--y[j],j}); } } if(i+m-1<=n) {//把i+m-1列的人的贡献加上 for(auto j:xpos[i+m-1]) { s.erase(s.lower_bound({y[j],j})); s.insert({++y[j],j}); } } for(auto j:xpos[i]) {//把第i列上的人的贡献删除 s.erase(s.lower_bound({y[j],j})); s.insert({--y[j],j}); } ans=max(ans,(*--s.end()).first+x[i]);//更新答案 for(auto j:xpos[i]) {//把第i列上的人的贡献加上 s.erase(s.lower_bound({y[j],j})); s.insert({++y[j],j}); } } return ans; } };
线段树解法
static const int maxn = 200052; int tr[maxn<<2]; void updata(int nl, int nr ,int now, int pos, int val) { if (nl == nr) { tr[now] += val; return; } int mid = (nl+nr) / 2; if (pos <= mid) { updata(nl, mid, now*2, pos, val); } else { updata(mid+1, nr, now*2+1, pos, val); } tr[now] = max(tr[now*2], tr[now*2+1]); } int query(int nl, int nr, int now, int l, int r) { if (l <= nl && nr <= r) { return tr[now]; } int mid = (nl+nr) / 2, ret = 0; if (mid >= l) { ret = max(ret, query(nl, mid, now*2, l, r)); } if (mid+1 <= r) { ret = max(ret, query(mid+1, nr, now*2+1, l, r)); } return ret; } vector<int> a[maxn]; int BoomKill(int n, int m, vector<Point>& Player) { // write code here int num[maxn] = {0}; for (int i=0; i<Player.size(); i++) { a[Player[i].x].push_back(i); num[Player[i].x]++; } int ans = 0, l = 1, r = 1, x = 0; while (x<n) { x++; while (r<=n && r <= x+m-1) { for (int i=0; i<a[r].size(); i++) { updata(1, n, 1, Player[a[r][i]].y, 1); } r++; } while (l<=n && l < x-m+1) { for (int i=0; i<a[l].size(); i++) { updata(1, n, 1, Player[a[l][i]].y, -1); } l++; } for (int i=0; i<a[x].size(); i++) { updata(1, n, 1, Player[a[x][i]].y, -1); } ans = max(ans, query(1, n, 1, 1, n) + num[x]); for (int i=0; i<a[x].size(); i++) { updata(1, n, 1, Player[a[x][i]].y, 1); } } return ans; }
解法2:TLE
枚举(x,y)点的位置,直接取计算答案。时间复杂度O(n^3),空间复杂度O(n*n)。
/** * struct Point { * int x; * int y; * }; */ const int N = 200001; class Solution { public: /** * * @param n int整型 n * @param m int整型 m * @param Player Point类vector Player * @return int整型 */ int BoomKill(int n, int m, vector<Point>& Player) { // write code here assert(1<=n&&n<=200000); assert(1<=m&&m<=200000); vector<vector<int>>mp(n+1, vector<int>(n+1)); for(auto i:Player) { assert(1<=i.x&&i.x<=n); assert(1<=i.y&&i.y<=n); mp[i.x][i.y]++; } int ans=0; for(int x=1;x<=n;x++) { for(int y=1;y<=n;y++) { int cnt=0; for(int i=1;i<=n;i++) cnt+=mp[x][i]; for(int i=max(1,x-m+1);i<=min(n,x+m-1);i++) cnt+=mp[i][y]; cnt-=mp[x][y]; ans=max(ans,cnt); } } return ans; } };
解法3:在解法1的基础上再做优化,不用set来维护,用一个链表来维护。链表上维护的是当前这列出现过的权值,并用链表从小到大存下来,可以把复杂度降到O(n),由于每次修改只会+1/-1,所以修改起来很简单。时间复杂度O(n),空间复杂度O(n),个人认为稍有难写,没有特别必要去这样写,用set蛮好的,当然如果实在要求O(n)可以这样写。
import java.util.*; import static java.lang.Math.max; import static java.lang.Math.min; /* * public class Point { * int x; * int y; * } */ public class Solution { /** * * @param n int整型 n * @param m int整型 m * @param Player Point类一维数组 Player * @return int整型 */ int N = 200001; public int BoomKill(int n, int m, Point[] Player) { // write code here List<Set<Integer>> numList = new ArrayList<>(); for (int i = 0; i < Player.length + 1; i++) { numList.add(new HashSet<>()); } Set<Integer> set = numList.get(0); for (int i = 1; i <= n; i++) { set.add(i); } //当前范围内每行影响数量 int[] numArray = new int[N]; //记录每行中的最大值和最大值对应的行 int max = 0, maxIndex = 0; List<Integer>[] xpos = new List[N]; for (int i = 0; i < xpos.length; i++) { xpos[i] = new ArrayList<>(); } //每列数量 int[] columnNum = new int[N]; for (Point i : Player) { xpos[i.x].add(i.y); columnNum[i.x]++; } for (int i = 1; i <= min(n, m); i++) { for (int j : xpos[i]) { numList.get(numArray[j]).remove(j); numArray[j]++; if (numArray[j] > max) { max = numArray[j]; maxIndex = j; } numList.get(numArray[j]).add(j); } } for (int j : xpos[1]) {//把第一列上的人的贡献删除 numList.get(numArray[j]).remove(j); numArray[j]--; numList.get(numArray[j]).add(j); } if (numArray[maxIndex] != max) {//更新max及index if (numList.get(max).size() != 0) { maxIndex = numList.get(max).iterator().next(); } else { max--; } } int ans = columnNum[1] + max; for (int j : xpos[1]) {//把第一列上的人的贡献加上 numList.get(numArray[j]).remove(j); numArray[j]++; if (numArray[j] > max) { max = numArray[j]; maxIndex = j; } numList.get(numArray[j]).add(j); } for (int i = 2; i <= n; i++) { if (i - m >= 1) {//把i-m列的人的贡献删去 for (int j : xpos[i - m]) { numList.get(numArray[j]).remove(j); numArray[j]--; numList.get(numArray[j]).add(j); } } if (numArray[maxIndex] != max) {//更新max及index if (numList.get(max).size() != 0) { maxIndex = numList.get(max).iterator().next(); } else { max--; } } if (i + m - 1 <= n) {//把i+m-1列的人的贡献加上 for (int j : xpos[i + m - 1]) { numList.get(numArray[j]).remove(j); numArray[j]++; if (numArray[j] > max) { max = numArray[j]; maxIndex = j; } numList.get(numArray[j]).add(j); } } for (int j : xpos[i]) {//把第i列上的人的贡献删除 numList.get(numArray[j]).remove(j); numArray[j]--; numList.get(numArray[j]).add(j); } if (numArray[maxIndex] != max) {//更新max及index if (numList.get(max).size() != 0) { maxIndex = numList.get(max).iterator().next(); } else { max--; } } ans = max(ans, max + columnNum[i]);//更新答案 for (int j : xpos[i]) {//把第i列上的人的贡献加上 numList.get(numArray[j]).remove(j); numArray[j]++; if (numArray[j] > max) { max = numArray[j]; maxIndex = j; } numList.get(numArray[j]).add(j); } } return ans; } }