codeforces 1353E 1353F DP
题目大意:给定一个长度为n的01串,给定一个值k,每次可以把该字符串中的任意一个字符变成0或者1,问至少操作多少次可以满足任意相邻两个1之间的长度为k。
这个题目的解法是dp,看过题解一开始也没看懂,这个要自己多模拟几遍,不然很有肯能看题解都看不懂(可能因为我菜叭)
dp[i][0]和dp[i][1]表示第i个字符为0或者1且前i个字符都满足的最小操作次数。
假设第i个字符为0(这个0是我们假设的,在原字符串中可能为0也可能为1),由于前i-1个字符是满足题意的,也就是说在前i个字符中,第i个字符是最后一个字符,那么一定满足题意,因为在01串的末尾不管加多少个0这个字符串都是合法的,所以dp[i][0]=min(dp[i-1][0],dp[i-1][1])+(str=='1'),也就是前i-1个满足条件的最小操作次数加上第i个字符是否需要改变。
假设第i个字符为1,有两种情况,第一种情况就是该字符前面存在有1,那么必须第i-k个字符为1,且前i-k个字符满足题意,并且第i-k+1个到第i-1个字符全为0才能满足题意。第二种情况就是该字符前面全是0,那么就要满足前i-1个字符全为0,即可满足题意,所以我们就要取这两种情况中操作次数较小的一种。
所以综合一下,我们可以得到dp的状态转移方程(ans[i]表示前i个字符中1的个数)
dp[i][0]=min(dp[i-1][0],dp[i-1][1])+(str[i-1]=='1');
dp[i][1]=min(ans[i-1],dp[p][1]+ans[i-1]-ans[p])+(str[i-1]=='0');
1 #include <iostream> 2 #include <cstring> 3 #include <string> 4 #include <algorithm> 5 #include <queue> 6 #include <stack> 7 #include <stdio.h> 8 #include <cmath> 9 #include <string.h> 10 11 using namespace std; 12 #define ll long long 13 static const int WHITE=0; 14 static const int GRAY=1; 15 static const int BLACK=2; 16 static const int INF=0x3f3f3f3f; 17 int gcd(int a,int b){return b == 0 ? a : gcd(b,a%b);} 18 int lcm(int a,int b){return a*b/gcd(a,b);} 19 ll Pow(ll a,ll b,ll mod){if(b==0) return 1%mod; ll sum=1; a=a%mod; while(b>0) { if(b%2==1) sum=(sum*a)%mod; b/=2; a=(a*a)%mod;}return sum;} 20 21 int dp[1000005][2]; 22 int ans[1000005]; 23 string str; 24 void init(int n) 25 { 26 for(int i=0;i<=n;i++) 27 dp[i][0]=dp[i][1]=0; 28 ans[0]=0; 29 for(int i=1;i<=str.size();i++) 30 ans[i]=ans[i-1]+(str[i-1]=='1'); 31 } 32 33 int main() 34 { 35 freopen("C:\\Users\\16599\\Desktop\\in.txt","r",stdin); 36 int _; 37 cin>>_; 38 while(_--) 39 { 40 int n,k; 41 cin>>n>>k; 42 cin>>str; 43 init(n); 44 for(int i=1;i<=str.size();i++) 45 { 46 int p=max(0,i-k); 47 dp[i][0]=min(dp[i-1][0],dp[i-1][1])+(str[i-1]=='1'); 48 dp[i][1]=min(ans[i-1],dp[p][1]+ans[i-1]-ans[p])+(str[i-1]=='0'); 49 } 50 cout<<min(dp[n][0],dp[n][1])<<endl; 51 } 52 return 0; 53 }