题目描述
难度分:1700
输入T(≤104)表示T组数据。所有数据的n×m之和≤106。
每组数据输入n,m(1≤n×m≤106)和长为n×m的01字符串s。
有n×m个人参加会议,会议厅的座位有n行m列。
从第一个人开始,依次入场。规则如下:
有人入场时,已入场的人同时向右移动一位,最右的人移动到下一行的最左边。(见样例)
第i个人标记为s[i]。
输出n×m个数,其中第i个数表示:第i个人入场后,至少有一个1的行数+至少有一个1的列数?
输入样例
3
2 2
1100
4 2
11001101
2 4
11001101
输出样例
2 3 4 3
2 3 4 3 5 4 6 5
2 3 3 3 4 4 4 5
样例的第一个数据如下
算法
滑动窗口+动态规划
统计列
s[i],s[i−m],s[i−2m],……是属于同一列的,因此只要出现s[i]=1,说明这一列就可以被计入答案了,用一个数组col1×m来标记每一列是否出现过1即可。
统计行
状态定义
dp[i]表示遍历到s[i]时有多少行是有1的。
状态转移
用一个长度为m的窗口window来记录第一行[i,i−m)内1的数量,这样状态转移方程就为
dp[i]=dp[i−m]+(window>0)
复杂度分析
时间复杂度
对于每个case,都只遍历了一遍字符串s就统计出了所有答案,因此时间复杂度为O(Tnm)。
空间复杂度
额外空间的瓶颈在于DP数组,其规模与s相同,因此额外空间复杂度为O(nm)(可以滚动数组优化到O(m))。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int T, n, m;
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &m);
string s;
cin >> s;
vector<int> col(m), dp(n*m);
int ccnt = 0, window = 0;
for(int i = 0; i < n*m; i++) {
if(s[i] == '1' && !col[i%m]) {
col[i%m] = true;
ccnt++;
}
window += s[i] == '1'? 1: 0;
if(i >= m) {
window -= s[i - m] == '1'? 1: 0;
}
if(i >= m) {
dp[i] = dp[i - m] + (window > 0? 1: 0);
}else {
dp[i] = window > 0? 1: 0;
}
printf("%d ", ccnt + dp[i]);
}
puts("");
}
return 0;
}