正所谓万物皆可dp
复杂度【O(2n)】
烧脑Dp题解奉上。
定义dp方程dp1[i][2] (0表示从前往后到第i位置为止,全都是0; 1表示从前往后到第i位置位置全是1? 并不是哦,1表示第i位置往前都是连续的1, 即00001111这种,i=8的位置往前都是连续的1)为i位置处理完后的最小操作次数。
dp2[i][2]同理, 只是方向反一下,是从后往前;
好,定义完了,开始给出递推方程式:
if (str[i] == '1') { dp1[i][1] = min(dp1[i - 1][1], dp1[i - 1][0]); dp1[i][0] = dp1[i - 1][0] + 1; } else { dp1[i][1] = min(dp1[i - 1][1] + 1, dp1[i - 1][0]); dp1[i][0] = dp1[i - 1][0]; }
对于当前i是1的时候:
最小的dp1[i][1]要么是连着前面的连续1, 要么就是连着前面的全0;
最小的dp1[i][0]只能是连着前面的全0,因为按定义上来讲,不可能连着连续1来;
对于i是0的情况:
最小的dp[i][1]中,我们将0去掉,然后连接dp[i -1][0]或者dp[i - 1][1]就可以了;
对于dp[i][0],直接连接上前面的全0;
dp2[i][j]的推到是同理,然后问题就迎刃而解了。
解决好dp后,只需要遍历字符串的每一个位置;
对于每个位置有4中情况;
要么前面全零,后面连续的1;
要么前面全零, 后面全零;
要么前面连续1, 后面全零;
要么前面连续1,后面全零;
mi = min(min(min(dp1[i - 1][0] + dp2[i][0], dp1[i - 1][0] + dp2[i][1]), min(dp1[i - 1][1] + dp2[i][0], dp1[i - 1][1] + dp2[i][1])), mi);
下面给出AC代码:
#include <iostream> #include<algorithm> #include<cstring> #include<cmath> #define lowbit(n) n&(-n) using namespace std; #define dbg(x) cout << #x << " = " << x << endl; const long long MAXN = 2e6; typedef long long ll; int t; ll n, m, k; char str[MAXN]; int dp1[MAXN][2], dp2[MAXN][2]; int main(int argc, char const *argv[]) { int n; cin >> n; cin >> (str + 1); int mi = 0x3f3f3f; for (int i = 1; i <= n; i++) { if (str[i] == '1') { dp1[i][1] = min(dp1[i - 1][1], dp1[i - 1][0]); dp1[i][0] = dp1[i - 1][0] + 1; } else { dp1[i][1] = min(dp1[i - 1][1] + 1, dp1[i - 1][0]); dp1[i][0] = dp1[i - 1][0]; } } for (int i = n; i >= 1; i--) { if (str[i] == '1') { dp2[i][1] = min(dp2[i + 1][1], dp2[i + 1][0]); dp2[i][0] = dp2[i + 1][0] + 1; } else { dp2[i][1] = min(dp2[i + 1][1] + 1, dp2[i + 1][0]); dp2[i][0] = dp2[i + 1][0]; } } for (int i = n; i >= 1; i--) { mi = min(min(min(dp1[i - 1][0] + dp2[i][0], dp1[i - 1][0] + dp2[i][1]), min(dp1[i - 1][1] + dp2[i][0], dp1[i - 1][1] + dp2[i][1])), mi); } cout << mi << endl; return 0; }