1. 程式人生 > >Codeforces 900 E. Maximum Questions (DP,技巧)

Codeforces 900 E. Maximum Questions (DP,技巧)

字符 但是 情況下 set using 前綴和 分享 出了 !=

題目鏈接:900 E. Maximum Questions

題意:

  給出一個長度為n只含有a和b還有‘?’的串s,且‘?‘可以被任意替換為a或b。再給出一個字符串t (奇數位上為a,偶數位上為b,所以在題目中只給出了t的長度m),現在要求保證t在s中出現的次數最多的情況下面,使用‘?‘最小的情況使用了幾個問號。

題解:

  拿到這個題目可以看出是DP,但是比較難以解決的就是如何確定在第i個位置時前面m個字符能夠滿足t,這裏我看了別人的題解 ~。~哇,震驚!首先的話先對‘?’做前綴和,然後設置兩個數組s[2]分別表示在第i個位置前面交叉字符串的長度,這樣的話第i步結尾為a(?)滿足交叉字符串的最大長度就等於第i-1步結尾為b(?)的長度加1;

1 res[i] = res[i-1] + (vec[i]==?);  // ?前綴和
2 if(vec[i] != a) s[0][i] = s[1][i-1] + 1; 
3 if(vec[i] != b) s[1][i] = s[0][i-1] + 1;

  這樣我們就求出了每一步是否滿足能夠形成t的條件,就可以開始DP了。(如果第i步滿足就判斷dp[i-m]+1和dp[i-1])

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int MAX_N  = 1e5+9;
 4 char vec[MAX_N];
5 int res[MAX_N]; 6 int s[2][MAX_N]; 7 int dp[MAX_N]; 8 int cost[MAX_N]; 9 int main() 10 { 11 int N,M,T; 12 memset(res,0,sizeof(res)); 13 scanf("%d%s%d",&N,vec+1,&M); // 這裏vec+1 可以把字符串賦予(1-n) 14 for(int i=1; i<=N; i++) 17 { 18 res[i] = res[i-1] + (vec[i]==?);
19 if(vec[i] != a) s[0][i] = s[1][i-1] + 1; 20 if(vec[i] != b) s[1][i] = s[0][i-1] + 1; 21 if(s[M&1][i] >= M) 22 { 23 dp[i] = dp[i-M]+1; 24 cost[i] = cost[i-M] + res[i] - res[i-M]; 25 } 26 else 27 { 28 dp[i] = dp[i-1]; 29 cost[i] = cost[i-1]; 30 } 31 if(dp[i] <= dp[i-1]) 32 { 33 if(dp[i] == dp[i-1]) cost[i] = min(cost[i],cost[i-1]); 34 else cost[i] = cost[i-1]; 35 } 36 } 37 cout<<cost[N]<<endl; 38 return 0; 39 }

emmmm...這是cf大佬的寫法~~ 也就比我短了一倍吧 @。@

技術分享圖片
 1 #include<bits/stdc++.h>
 2 #define N 100005
 3 using namespace std;
 4 char s[N];
 5 int n,m,b[N],c[2][N];
 6 pair<int,int> f[N];
 7 int main(){
 8     scanf("%d%s%d",&n,s+1,&m);
 9     for (int i=1;i<=n;i++){
10         b[i]=b[i-1]+(s[i]==?);
11         if (s[i]!=a) c[0][i]=c[1][i-1]+1;
12         if (s[i]!=b) c[1][i]=c[0][i-1]+1;
13         if (c[m&1][i]>=m) f[i]=make_pair(f[i-m].first+1,f[i-m].second-b[i]+b[i-m]);
14         f[i]=max(f[i],f[i-1]);
15     }
16     printf("%d\n",-f[n].second);
17 }
View Code

Codeforces 900 E. Maximum Questions (DP,技巧)