题意:
有n*m的矩阵,然后你有k发子弹。现在你可以朝着任意列发射子弹,每一发子弹都会使该列上的数值-1,最小减少到0。
现在问你连续最长的行数,在k发子弹内,使得这些行上的数值全部为0.
思路:
看了别人代码,其实RMQ不是必要的,开m个双端队列也可以。因此每次要问一段范围内的最大值都是按顺序下去的,队列可以解决。
二分长度len,枚举n行是否存在这样的i~i+len-1,所需要的子弹数<=k,存在则表示len可行。
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 1e5+5;
const int MAXM = 5;
int a[MAXN][5];
int dp[5][MAXN][20]; //five RMQ
int res[MAXM], tres[MAXM];
int n, m, k;
void RMQ(int t) {
for(int i = 0;i < n; i++)
dp[t][i][0] = a[i][t];
for(int j = 1;(1<<j) <= n; j++)
for(int i = 0;i + (1<<j)-1<n; i++)
dp[t][i][j] = max(dp[t][i][j-1], dp[t][i+(1<<(j-1))][j-1]);
}
int RmqQuery(int t, int l, int r) {
int k = 0;
while((1<<(k+1)) <= r-l+1) k++;
return max(dp[t][l][k], dp[t][r-(1<<k)+1][k]);
}
bool check(int mid) {
for(int i = 0;i+mid-1 < n; i++) {
int sum = 0;
for(int j = 0;j < m; j++) {
tres[j] = RmqQuery(j, i, i+mid-1);
sum += tres[j];
}
if(sum <= k) {
for(int j = 0;j < m; j++)
res[j] = tres[j];
return true;
}
}
return false;
}
void solve() {
for(int i = 0;i < m; i++)
RMQ(i);
//bs
int l = 0, r = n;
int len = 0;
while(l <= r) {
int mid = (l+r)/2;
if(check(mid)) {
l = mid+1;
len = mid;
}
else r = mid-1;
}
//output
for(int i = 0;i < m; i++)
printf("%d ", res[i]);
puts("");
}
int main() {
scanf("%d%d%d", &n, &m, &k);
for(int i = 0;i < n; i++)
for(int j = 0;j < m; j++)
scanf("%d", &a[i][j]);
solve();
return 0;
}