社团游戏【二分 + 递推】

题目描述

在民风淳朴的雏见泽,号称能“完美犯罪”的天才牛牛,又开始和社团的萌妹子牛妹玩起了游戏。

在今天的游戏中,牛牛将会得到一个n×mn\times mn×m且全为小写字母的矩阵,他可以从矩阵中任选一块正方形,但必须保证该正方形中任意一类小写字母个数之和不能超过kkk,换而言之,在该正方形中,‘a’字符个数不能超过kkk,‘b’字符个数不能超过kkk,…,‘z’字符个数不能超过kkk。

现在牛牛想知道,以(i,j)(i,j)(i,j)为左上角且符合以上要求的正方形中,边长最大的是多少?

输入描述:

第一行三个正整数nnn,mmm,kkk,其中:n≤500n\leq500n≤500,m≤500m\leq500m≤500,k≤109k\leq10^{9}k≤109。

接下来nnn行,每行mmm个小写字母。

输出描述:

输出nnn行,每行mmm个数字。其中第iii行第jjj个数字表示,以(i,j)(i,j)(i,j)为左上角且符合题目要求的正方形的最大边长。

示例1
输入
复制

3 3 2
aaa
bcd
efg

输出
复制

2 2 1
2 2 1
1 1 1

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;

const int maxn = 1e3 + 10;
int dp[30][maxn][maxn];
char a[maxn][maxn];
int ans[maxn][maxn];
int n, m, k;

void init() {
	for(int k = 0; k < 26; k++) {
		for(int i = 1; i <= n; i++) {
			for(int j = 1; j <= m; j++)
				dp[k][i][j] = dp[k][i - 1][j] + dp[k][i][j - 1] - dp[k][i - 1][j - 1] + (a[i][j] == k + 'a');
		}
	} 
}

bool judge(int x1, int y1, int x2, int y2) {
	for(int i = 0; i < 26; i++) {
		if(dp[i][x2][y2] - dp[i][x1 - 1][y2] - dp[i][x2][y1 - 1] + dp[i][x1 - 1][y1- 1] > k)
			return false;
	}
	return true;
}

void solve() {
	for(int i= 1; i <= n; i++) {
		for(int j =1 ; j <= m; j++) {
			int l = 1, r = min(n - i + 1, m - j + 1), ans = 1;
			while(l <= r) {
				int mid = l + r >> 1;
				if(judge(i, j, i + mid - 1, j + mid - 1))
					l = mid + 1, ans = mid;
				else
					r = mid - 1;
			}
			cout << ans << " ";
		}
		cout << endl;
	}
}

int main() {
	cin >> n >> m >> k;
	for(int i = 1; i <= n; i++)
		cin >> a[i] + 1;
	init();
	solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值