题目描述
难度分:1600
输入n,m(1≤n×m≤105)和n行m列的矩阵a,元素范围[1,109]。
然后输入k(1≤k≤105)和k个询问,每个询问输入两个数L,R(1≤L≤R≤n)。
对于每个询问,请判断在第L行到第R行中,是否存在一列,元素值从上到下是递增的(允许相等),输出Yes
或No
。
输入样例
5 4
1 2 3 5
3 1 3 2
4 5 2 3
5 5 3 2
4 4 3 4
6
1 1
2 5
4 5
3 5
1 3
1 5
输出样例
Yes
No
Yes
Yes
Yes
No
算法
动态规划
状态定义
up[i][j]表示在第j列中,从第l行到第i行都能保持单调不减的最小l。
状态转移
分为两种情况来讨论
- 如果a[i][j]<a[i−1][j],状态转移方程为up[i][j]=i,表示只有第i行到第i行满足单调不减。
- 否则状态转移方程为up[i][j]=up[i−1][j],把前一行的答案延续过来。
在读入数据遍历j的时候,可以维护minL[i]=minj∈[1,m]up[i][j]。随后在面对询问[L,R]时,只要minL[R]≤L,就说明存在一列使得从第L行到第R行单调不减。
复杂度分析
时间复杂度
动态规划在读入数据的时候就可以顺便做完,时间复杂度为O(nm)。后续的k个查询,每个查询的时间复杂度都是O(1),所以查询的时间复杂度为O(k)。因此,算法整体的时间复杂度为O(nm+k)。
空间复杂度
空间消耗的瓶颈主要在于DP
数组up,规模是nm的,因此额外空间复杂度为O(nm)。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n, m;
cin >> n >> m;
vector<int> minL(n + 1);
vector<vector<int>> mat(n + 1, vector<int>(m + 1)), up(n + 1, vector<int>(m + 1));
for(int i = 1; i <= n; i++) {
minL[i] = i;
for(int j = 1; j <= m; j++) {
cin >> mat[i][j];
if(i == 1 || mat[i][j] < mat[i - 1][j]) {
up[i][j] = i;
}else {
up[i][j] = up[i - 1][j];
}
minL[i] = min(minL[i], up[i][j]);
}
}
int k;
cin >> k;
for(int i = 1; i <= k; i++) {
int l, r;
cin >> l >> r;
if(minL[r] <= l) {
cout << "Yes" << endl;
}else {
cout << "No" << endl;
}
}
return 0;
}