1. 程式人生 > 實用技巧 >CodeForces - 253D Table with Letters - 2 (有技巧的暴力列舉)

CodeForces - 253D Table with Letters - 2 (有技巧的暴力列舉)

題目連結:https://codeforces.com/problemset/problem/253/D

題目大意:對於一個字元矩陣,找其中的子塊,要求有兩個其一子塊中包含字元a的個數不超過k,其二子塊的四個角的字元相同。且子塊的行數大於等於2,列數大於等於2

Examples

Input
3 4 4
aabb
baab
baab
Output
2
Input
4 5 1
ababa
ccaca
ccacb
cbabc
Output
1

emmm,首先,最為暴力的方法就是直接列舉上邊界、下邊界、左邊界和右邊界,那麼複雜度為$O(n^4)$會T掉,暴力寫法如下:

#include <cstdio>
#include 
<cstring> #include <algorithm> #include <cmath> #include <iostream> using namespace std; const int mac=450; char s[mac][mac]; int as[mac][mac]; int main(int argc, char const *argv[]) { freopen("input.txt","r",stdin); freopen("output.txt","w",stdout); ios::sync_with_stdio(
false); cin.tie(0);cout.tie(0); int n,m,lim; cin>>n>>m>>lim; for (int i=1; i<=n; i++) cin>>s[i]+1; for (int i=1; i<=n; i++) for (int j=1; j<=m; j++) as[i][j]=as[i-1][j]+as[i][j-1]-as[i-1][j-1]+(s[i][j]=='a'?1:0); int
ans=0; for (int i=1; i<n; i++){//列舉上行 for (int j=i+1; j<=n; j++){//列舉下行 for (int l=1; l<m; l++){//列舉左邊界 for (int r=l+1; r<=m; r++){//列舉右邊界 if (as[j][r]-as[i-1][r]-as[j][l-1]+as[i-1][l-1]>lim) continue; if (s[i][l]==s[i][r] && s[i][r]==s[j][r] && s[j][r]==s[j][l]) ans++; } } } } printf("%d\n",ans); return 0; }
View Code

接下來我們就是想辦法優化它了。列舉左邊界的時候判斷左上角和左下角的字元,優化一下,接下來就是對該列該寬度下的上下字元一樣的情況,我們用陣列來儲存每個字元在的合法數量,直接將該字元對應的ASCLL位置進行+1操作:

for (int l=1; l<m; l++) { //列舉左邊界
    if (s[i][l]!=s[j][l]) continue;
    cnt[s[i][l]]--;
    while (r<=m && as[j][r]-as[i-1][r]-as[j][l-1]+as[i-1][l-1]<=lim) {
        if (s[i][r]==s[j][r]) cnt[s[j][r]]++;
        r++;
    }
    if (cnt[s[i][l]]>0) ans+=cnt[s[i][l]];
}

這樣就大大優化了時間,其中cnt陣列剛開始-1是由於r也是從1開始的,他需要去一下重。

那麼優化後的程式碼也就出來了:

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

typedef long long ll;
const int mac=450;

char s[mac][mac];
int as[mac][mac],cnt[200];

int main(int argc, char const *argv[])
{
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int n,m,lim;
    cin>>n>>m>>lim;

    for (int i=1; i<=n; i++)
        cin>>s[i]+1;
    for (int i=1; i<=n; i++)
        for (int j=1; j<=m; j++)
            as[i][j]=as[i-1][j]+as[i][j-1]-as[i-1][j-1]+(s[i][j]=='a'?1:0);

    ll ans=0;
    for (int i=1; i<n; i++){//列舉上行
        for (int j=i+1; j<=n; j++){//列舉下行
            memset(cnt,0,sizeof cnt);
            int r=1;
            for (int l=1; l<m; l++){//列舉左邊界
                if (s[i][l]!=s[j][l]) continue;
                cnt[s[i][l]]--;
                while (r<=m && as[j][r]-as[i-1][r]-as[j][l-1]+as[i-1][l-1]<=lim){
                    if (s[i][r]==s[j][r]) cnt[s[j][r]]++;
                    r++;
                }
                if (cnt[s[i][l]]>0) ans+=cnt[s[i][l]];
            }
        }
    }
    printf("%lld\n",ans);
    return 0;
}