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!!

posted @ 2026-01-29 20:11  zhangruotian_Max  阅读(1)  评论(0)    收藏  举报