传送门
题意:给出一个矩阵,格子有黑白两种,同时给你一个边长为k的矩阵,你可以使得一个边长为k的矩阵变为全白,使得白线(一行全白或一列全白)的个数最多。
解:没想到什么好办法,感觉就是暴力了吧,我的想法是先处理每个点是否可以到达上界,左界,右界和下界,同时将原本存在的白线先计算了,考虑矩阵平移,考虑列,每次就增加一列,减少一列,所以我们O(1)考虑这个贡献就行了考虑行,每次下移也就增加一行减少一行,O(1)转移,所以我们只需在算第一行和第一列的时候,进行暴力计算即可。常数比较大的O(n^2).而且我这码量不小。
码量小的在下面T_T
#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int maxn=2e3+5;
//il int Add(int x,int y) {return x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll x,int y) {return x*y>=mod?x*y%mod:x*y;}
int n,k;
int mp[maxn][maxn];
int up[maxn][maxn],down[maxn][maxn],le[maxn][maxn],ri[maxn][maxn];
bool fgud[maxn],fglr[maxn];
int init() {
int ans=0;
for(int i=1; i<=n; ++i) {
bool fg1=0,fg2=0;
for(int j=1; j<=n; ++j) {
if(!mp[i][j]) {
fg1=1;
break;
}
}
if(!fg1) ans++,fglr[i]=1;
for(int j=1; j<=n; ++j) {
if(!mp[j][i]) {
fg2=1;
break;
}
}
if(!fg2) ans++,fgud[i]=1;
}
return ans;
}
void gao() {
for(int i=0; i<=n+1; ++i) up[0][i]=1,down[n+1][i]=1,le[i][0]=1,ri[i][n+1]=1;
for(int i=1; i<=n; ++i) {
for(int j=1; j<=n; ++j) {
if((up[i-1][j]==1) && mp[i][j]==1) up[i][j]=1;
if((le[i][j-1]==1) && mp[i][j]==1) le[i][j]=1;
}
}
for(int i=n; i>=1; --i) {
for(int j=n; j>=1; --j) {
if((down[i+1][j]==1) && mp[i][j]==1) down[i][j]=1;
if((ri[i][j+1]==1) && mp[i][j]==1) ri[i][j]=1;
}
}
}
int updown[maxn];
int main() {
std::ios::sync_with_stdio(0);
cin>>n>>k;
string s;
for(int i=1; i<=n; ++i) {
cin>>s;
for(int j=1; j<=n; ++j) {
if(s[j-1]=='W') mp[i][j]=1;
else mp[i][j]=0;
}
}
int ans=init();
gao();
int res=ans,preans=ans;
// cout<<"pre "<<preans<<endl;
for(int i=1; i+k-1<=n; ++i) {
int nl=i,nr=i+k-1,nup=1,ndown=1+k-1;
if(nl==1) {
for(int j=nl; j<=nr; ++j) {
if((up[nup-1][j]) && (down[ndown+1][j]) && !fgud[j]) ans++;
}
for(int j=nup; j<=ndown; ++j) {
if((le[j][nl-1]) && (ri[j][nr+1]) && !fglr[j]) ans++,updown[i]++;
}
res=max(res,ans);
ans-=updown[i];
} else {
if((up[nup-1][nl-1]) && (down[ndown+1][nl-1]) && !fgud[nl-1]) ans--;
if((up[nup-1][nr]) && (down[ndown+1][nr]) && !fgud[nr]) ans++;
for(int j=nup; j<=ndown; ++j) {
if((le[j][nl-1]) && (ri[j][nr+1]) && !fglr[j]) ans++,updown[i]++;
}
res=max(res,ans);
ans-=updown[i];
}
}
// cout<<"res "<<res<<endl;
for(int t=2; t+k-1<=n; ++t) {
ans=preans;
for(int i=1; i+k-1<=n; ++i) {
int nl=i,nr=i+k-1,nup=t,ndown=t+k-1;
if(nl==1) {
for(int j=nl; j<=nr; ++j) {
if((up[nup-1][j]) && (down[ndown+1][j]) && !fgud[j]) ans++;
}
if(le[nup-1][nl-1] && ri[nup-1][nr+1] && !fglr[nup-1]) updown[i]--;
if(le[ndown][nl-1] && ri[ndown][nr+1] && !fglr[ndown]) updown[i]++;
ans+=updown[i];
res=max(res,ans);
ans-=updown[i];
}
else{
if(le[nup-1][nl-1] && ri[nup-1][nr+1] && !fglr[nup-1]) updown[i]--;
if(le[ndown][nl-1] && ri[ndown][nr+1] && !fglr[ndown]) updown[i]++;
ans+=updown[i];
if((up[nup-1][nl-1]) && (down[ndown+1][nl-1]) && !fgud[nl-1]) ans--;
if((up[nup-1][nr]) && (down[ndown+1][nr]) && !fgud[nr]) ans++;
res=max(res,ans);
ans-=updown[i];
}
}
}
cout<<res<<endl;
return 0;
}
赛后看别人的代码,浓缩的思想呀。
r[i][j]:第i行前j出现B的个数;
c[i][j]:第i列前j出现B的个数;
rr[i][j]: 前i行第j个矩阵(i,j为左顶点),新产生的白线 的前缀和
cc[i][j]: 前j列的第i个矩阵(i,j为左顶点),新产生的白线 的前缀和
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e3 + 10;
char s[N][N];
int r[N][N],c[N][N],rr[N][N],cc[N][N];
int main(){
int n,k;
scanf("%d%d",&n,&k);
for (int i = 1; i <= n; i++)
scanf("%s",s[i]+1);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
r[i][j] = r[i][j-1]+(s[i][j]=='B'); //i行
c[i][j] = c[i][j-1]+(s[j][i]=='B'); //i列
}
}
int tot = 0;
for (int i = 1; i <= n; i++)
tot+=(r[i][n] == 0) + (c[i][n] == 0);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n-k+1; j++) {
rr[i][j] = rr[i-1][j] + (r[i][j+k-1] - r[i][j-1] == r[i][n] && r[i][n]);
cc[i][j] = cc[i-1][j] + (c[i][j+k-1] - c[i][j-1] == c[i][n] && c[i][n]);
}
}
int ans = tot;
for (int i = 1; i <= n-k+1; i++)
for (int j = 1; j <= n-k+1; j++)
ans = max(ans,tot+(rr[i+k-1][j]-rr[i-1][j])+(cc[j+k-1][i]-cc[j-1][i]));
printf("%d\n", ans);
return 0;
}