ABC442_F Diagonal Separation 2
442是我第一次场切5个题!太开心了,写个题解记录一下
正题开始
不难发现,最后的涂黑的格子一定是从左到右成阶梯状上升。格式化的讲:设\(a_i\)表示第\(i\)列从下到上有几个黑色的格子,且上半部分是白格,下半部分是黑格,则有\(0≤a_1≤a_2≤...≤a_n\)
为什么是这样的呢,因为如果有\(a_i≤a_{i+1}\)那么肯定有一行就不可能满足左边全是白格,右边是全是黑格。
所以,我们对于每一列,从下往上去看,连续涂黑\(n\)格需要多少步数。定义:\(p_{i,j}\)表示第\(i\)列从下到上涂到\(n-j+1\)。特别的,当\(j\)等于\(0\)的时,\(p_{i,j}=0\)。
那么现在就可以统计答案了,不难想到\(dp\)。如果要暴搜的话,肯定过不了。我们定义:\(dp_{i,j}\)表示考虑完前\(i\)列,最高选到\(j\)的最小值是多少。转移方程为:\(dp_{i,j}=min(dp_{i-1,0...j})+a_j\),时间复杂度是\(O(n^3)\)。过不了\(1≤n≤5000\)。瓶颈在于求\(min\),时间复杂度是\(O(n)\)的,要降到\(O(1)\),不难想到预处理。
我们定义:\(mx_{i,j}\)表示\(min(dp_{i-1,0...j})\)。在做\(dp\)的时候,边做边更新。即\(mx_{i,j}=min(mx_{i,j-1},dp_{i,j})\)
所以转移方程变为\(dp_{i,j}=mx_{i,j}+a_j\)。那么这个题目就顺利解决了,答案就是\(min(dp_{n,0...j})\)。
代码如下
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define int long long
using vt=vector<int>;
using ll=long long;
using vl=vector<ll>;
const int mod=998244353;
const int P=1e9+7;
const int N=5010;
char s[N][N];
int n,p[N][N],dp[N][N],mx[N][N];
#define bug cout<<"bug is here!"<<endl;
void solve(){
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
for(int i=1;i<=n;i++){
int sum=0;
for(int j=1;j<=n;j++) sum+=(s[j][i]=='#');
int bb=0,ub=sum;
p[i][0]=ub;
for(int j=1;j<=n;j++){
if(s[n-j+1][i]=='#') bb++,ub--;
p[i][j]=j-bb+ub;
}
}
memset(mx,127,sizeof mx);
for(int i=0;i<=n;i++) mx[1][i]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=n;j++){
dp[i][j]=mx[i][j]+p[i][j];
mx[i+1][j]=min(mx[i+1][max(0ll,j-1)],dp[i][j]);
}
}
int ans=LLONG_MAX;
for(int i=0;i<=n;i++) ans=min(ans,dp[n][i]);
printf("%lld\n",ans);
}
signed main(){
//cin.tie(0)->sync_with_stdio(0);
int t=1;
//cin>>t;
while(t--) solve();
}
okk!!

浙公网安备 33010602011771号