Codeforces Round #642 (Div. 3) 参与排名人数11823
[codeforces 1353E] K-periodic Garland 为什么会想到动归dp
总目录详见https://blog.csdn.net/mrcrack/article/details/103564004
在线测评地址http://codeforces.com/contest/1353/problem/E
Problem | Lang | Verdict | Time | Memory |
---|---|---|---|---|
E - K-periodic Garland | GNU C++17 | Accepted | 46 ms | 12400 KB |
思路来自对数据的分析:
以n=4,k=2为例
有16种形态
0000符合题意
0001符合题意
0010符合题意
0011
0100符合题意
0101符合题意
0110
0111
1000符合题意
1001
1010符合题意
1011
1100
1101
1110
1111
动归dp的目标不是枚举所有情况,而是符合题意的情况一个都不会漏,符合题意的形态有7种
0000
0001
0010
0100
0101
1000
1010
通过对以下数据进行归纳,容易得出对应的动归dp的写法,对这7种形态,归类,找规律
1000注意,最后一个1,后面全是0
1010注意,最后一个1,后面全是0.(1010与1000有递推关系)
0100注意,最后一个1,后面全是0
0101注意,最后一个1,后面正好数据据终结.(0101与0100有递推关系)
0010注意,最后一个1,后面全是0
0001注意,最后一个1,后面正好数据据终结
0000注意,全是0
考虑如下输入数据
4 2
1111
1111变换到0000,需变动4个数据
1111变换到1000,需变动3个数据
1111变换到1010,需变动2个数据
1111变换到0100,需变动3个数据
1111变换到0101,需变动2个数据
1111变换到0010,需变动3个数据
1111变换到0001,需变动3个数据
取最小值2,
输出2
根据上述数据分析,容易得到动归dp对应的算法如下:
dp[i]记录字串[1,i]区间满足题意的周期性质,第i位为必须为1,需要更新原字符串中的值的最小个数(1变0,记1次;0变1,记1次)
AC代码如下
#include <cstdio>
#include <algorithm>
#define maxn 1000010
using namespace std;
char s[maxn];
int sum[maxn],dp[maxn];//dp[i]记录字串[1,i]区间满足题意的周期性质,第i位为必须为1,需要更新原字符串中的值的最小个数(1变0,记1次;0变1,记1次)
int main(){
int t,n,k,i,ans;
scanf("%d",&t);
while(t--){
scanf("%d%d%s",&n,&k,s+1);
for(i=1;i<=n;i++)sum[i]=sum[i-1]+s[i]-'0';//sum[]采用前缀和的方式记录字符串中1的个数
for(i=1;i<=n;i++)dp[i]=sum[i-1]+(s[i]=='0');//处理字串[1,i]区间,目标(第i位为1,[1,i-1]全为0),字串需要变动的个数
for(i=k+1;i<=n;i++)//要涉及起始位置,以及之后一个周期的k个数,故每次需处理连续的k+1个数
dp[i]=min(dp[i],dp[i-k]+sum[i-1]-sum[i-k]+(s[i]=='0'));//dp[i-k]对应字串[1,i-k]区间满足题意的周期性质,第i-k位为必须为1,需要更新原字符串中的值的最小个数(1变0,记1次;0变1,记1次).sum[i-1]-sum[i-k]对应原字串区间[i-k+1,i-1]中1的个数.(s[i]=='0'),对应,第i位为必须为1,该位必须更新的次数。
ans=sum[n];//对应更新后的字串全是0,需要更新的个数,即原字串中的1全部变更为0
for(i=1;i<=n;i++)
ans=min(ans,dp[i]+sum[n]-sum[i]);//请注意,区间[i+1,n]原字串的1全部变更为0.sum[n]-sum[i]对应原字串区间[i+1,n]中1的个数.
printf("%d\n",ans);
}
return 0;
}
来源:oschina
链接:https://my.oschina.net/u/4360480/blog/4282142