CodeForces - 253D Table with Letters - 2 (有技巧的暴力列舉)
阿新 • • 發佈:2020-07-09
題目連結:https://codeforces.com/problemset/problem/253/D
題目大意:對於一個字元矩陣,找其中的子塊,要求有兩個其一子塊中包含字元a的個數不超過k,其二子塊的四個角的字元相同。且子塊的行數大於等於2,列數大於等於2
Examples
Input3 4 4Output
aabb
baab
baab
2Input
4 5 1Output
ababa
ccaca
ccacb
cbabc
1
emmm,首先,最為暴力的方法就是直接列舉上邊界、下邊界、左邊界和右邊界,那麼複雜度為$O(n^4)$會T掉,暴力寫法如下:
#include <cstdio> #includeView Code<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); intans=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; }
接下來我們就是想辦法優化它了。列舉左邊界的時候判斷左上角和左下角的字元,優化一下,接下來就是對該列該寬度下的上下字元一樣的情況,我們用陣列來儲存每個字元在的合法數量,直接將該字元對應的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; }