【算法学习笔记】42.正反DP 填充连续 SJTU OJ 1285 时晴时雨
1285. 时晴时雨
Description
Taring 喜欢晴天,也喜欢雨天。
Taring说:我想体验连续的K天的晴朗,去远足,去放歌;我还想再这K个晴天之后,再去体验连续的K天的云雨,去感受落雨时的轻语。这是令Taring最开心的事情了。其它的时间,Taring会在机房默默的编写着代码。
当然,Taring不想在这连续的K个晴天和连续的K个雨天里被机房的事务打扰或者被自然天气的变化中断。也就是说,这K个晴天和K个雨天必须是连续的,但是他们之间并不需要时间连续。显然的,Taring如果在感受了连续的K天晴朗后,前去机房写代码,等待下雨的时候再次开始他的感悟,这样是不会影响Taring的心情的。
Taring通过天气预报得到了最近连续 N 天中若干天的天气情况(天气情况只有“晴”和“雨”两种),但其他的天数的天气情况均是未知的。
他想知道有多少种可能的不同的天气情况(对于两种天气情况,如果有任意一天的天气情况是不一样的,就算做不同的天气情况),使他能够完成这两项体验。
Input Format
输入共有两行。
第1行有2个整数,分别表示上文中所述的N和K。
第2行是一个长度为N的字符串,仅包含有B(清),W(雨),X(未知)三种字符。
Output Format
输出一个整数Ans,表示合法的天气方案数量。
由于Ans可能很大,请将答案Mod 1,000,000,007 (109+7)
Sample Input 1
4 2
XXXX
Sample Output 1
1
Sample Input 2
10 2
XXBXXWXXXX
Sample Output 2
166
原题:http://codeforces.com/contest/204/problem/D
官方题解:http://codeforces.com/blog/entry/4849
根据官方题解分析 大致思路如下:
//b[i] 顺序DP 表示到i位置为止 没有出现k个连续的B的填充方法数
//c[i] 顺序DP 表示到i位置位置 恰好第一次出现连续k个B的填充方法数
//w_1[i] 逆序DP 表示从n开始到i位置 没有出现k个连续的W的填充方法数
//w_2[i] 逆序DP 表示从n开始到i位置 出现了k个连续的W填充方法数
//total[i] 逆序DP 表示从n位置开始到i位置结束时 一共有多少种填充方法
第1步 求b[]
首先 初始化 b[0] = 1; 还有S[0]=‘0‘;//这一步是为了以后的判断比较方便
1.1 判断当前处理位置的类型
1.1.1 如果遇到不确定位 X ,则让b[i]= 2 * b[i-1] 因为此处可以有两种填充方法, 当然可能会产生一些错误的填充 下面进行处理
1.1.2 如果遇到的是确定位 则让b[i] = 1 * b[i-1] 因为只有一种填充方法, 当然也会产生一些错误的填充
1.2 记录当前位置连续的B的个数 continous_b[i]
1.3 如果 continous_b[i]>=k 和 S[i-k]不是‘B‘
//来计算错误的填充数目 bad
//由于是DP过程,我们假设b[0]~b[i-1]的计算都是正确的, 则错误的填充指的是 从i-k开始 到 i位置 恰好是连续k个B的情况
//根据样例 则是 xxxxxWBB 的情况, 前面的xxxxx的填充数 就是 b[i-k-1] 后面的WBB是固定的部分 * 1
所以 bad = 1 * b[i-k-1]
(注意特殊情况 如果i=k 也就是恰好前k个字符可以实现k个B的情况, 则让bad为1即可)
最后让 b[i] = b[i] - bad;
第2步 求c[]
2.1 c[0]=0
2.2 c[i] 其实就是第1步里的bad 不同之处就是如果不满足那个情况 则要另c[i]=0
//如果把c[]的计算和b[]的融合在一起 则不需要continous_b数组 只需要一个数即可
第3步 求w_1[]
和第1步一样的思想,不同的方向而已
第4步 求total[]
逆序 遇到X则累成2
第5步 求w_2[]
把total和w_1相减即可 //补集的思想
第6部 求最终结果ans
这里要注意: 我们已经得到了c[]和w[], c[]表示在i位置处 第一次出现连续k个B的填充方法数目, w[]表示后j个中 出现连续k个W的方法数目,可见 这要遍历i从1到n
ans += c[i] * w[i+1]即可,原因在于 c[]已经完成了去重的工作!
PS: mod时,有一个细节要注意就是 减法 的取模 要 先加一个mod再取模, 原因:余数之间的减法操作可能会导致负数的存在!
代码如下:
#include <iostream> using namespace std; const int MaxN = (int) 1e6+5; const int Mod = (int) 1e9+7; typedef unsigned long long ULL; char S[MaxN]; ULL b[MaxN],c[MaxN],w[MaxN],continous_b[MaxN],total[MaxN]; //continous_b记录的是到i位置时 恰好出现了连续的k个B的情况数目 不用也行 int main(int argc, char const *argv[]) { int n,k; cin>>n>>k; S[0]=‘0‘;//为了在计算b的时候 临界情况的判断 cin>>(S+1);//为了下标对齐,从1开始输入 //求b[] b[i]表示的是 从1开始到i位置 没有出现过连续的k个B的情况的个数 b[0] = 1; for (int i = 1; i <= n; ++i) { //如果是X 由于可以填2种 所以乘2 注意:此时的b[i]是有bad filling的 //这个bad filling就是指, 前i个位置 有连续k个B //假设之前的b 全是正确的 那么这个bad filling 只有一种情况 那就是刚好填了B且满足前k个都是B b[i] = ( S[i]==‘X‘ ) ? 2 * b[i-1] % Mod : 1 * b[i-1]; continous_b[0]=0; //为了找出是否此时出现了 bad filling 我们要记录到i为止 前面出现的连续的B的个数 switch(S[i]){ case ‘X‘: case ‘B‘: // 可继 continous_b[i] = continous_b[i-1]+1; break; case ‘W‘: //断了 continous_b[i] = 0; continue;//可以下一次循环了 因为肯定不是bad filling } //判断是否出现了bad filling if(continous_b[i] >= k and S[i-k]!=‘B‘){ //正好出现了连续的k个B //此处 右边的 1 * b[i-k-1] 中的1表示的是WBB的情况:只有一种,b[i-k-1]表示的是前i-k-1中没有连续k个B的情况 //##### 重点 : 两者相乘就是所谓的bad fillings int bad ; if (i > k){ bad = 1 * b[i-(k+1)]; }else if(i==k){ bad = 1 * 1;//特殊临界情况 } c[i] = i-k-1>=0 ? b[i-k-1] : 1 ; b[i] = (b[i] - bad + Mod) % Mod; }else c[i] = 0; }//此时已经计算了 b[]和continous_b[] //求c[] ci表示的是到第i个位置 恰好出现了连续的k个B的情况数 (目的是为了去重) 也可以放在上一个循环里面做 // c[0] = 0; // for (int i = 1; i <= n; ++i) // { // if(continous_b[i]>=k and S[i-k]!=‘B‘) // c[i] = i-k-1>=0 ? b[i-k-1] : 1 ; // else // c[i] = 0; // } //现在求w[i], w[i]表示的是从最后一个位置开始 到i截止 有k个连续的W的情况数 和第一步类似 最后加上一部补就好了 //现在的w[i]还不是真正的结果 w[n+2] = 1;//为了方便 w[n+1] = 1; S[n+1] = ‘0‘; int continous_w = 0; for (int i = n; i >=0; --i) { w[i] = (S[i]==‘X‘) ? 2 * w[i+1] % Mod : w[i+1]; switch(S[i]){ case ‘X‘: case ‘W‘: continous_w++; break; case ‘B‘: continous_w = 0; continue;//不用判断是否有bad fullings了 } if(continous_w >= k and S[i+k] != ‘W‘) w[i] = (w[i] - w[i+k+1] + Mod) % Mod; } //记录全部情况的数量 total[n+1]=1; for (int i = n; i >=0; --i) { total[i] = (S[i]==‘X‘) ? 2 * total[i+1] % Mod : total[i+1]; } //变换为补集 for (int i = 1; i <= n; ++i) w[i] = (total[i] - w[i] + Mod) % Mod; ULL ans = 0; for (int i = 1; i <= n-1; ++i){ ans = (ans + c[i] * w[i+1]) % Mod; } cout<<ans<<endl; // for (int i = 1; i <= n; ++i) // { // cout<<b[i]<<" "; // }cout<<endl; // for (int i = 1; i <= n; ++i) // { // cout<<c[i]<<" "; // }cout<<endl; // for (int i = 1; i <= n; ++i) // { // cout<<w[i]<<" "; // }cout<<endl; return 0; } /* 10 2 XXBXXWXXXX 3 2 */
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。