【算法学习笔记】42.正反DP 填充问题 SJTU OJ 1285 时晴时雨
2015-05-07 08:59
281 查看
1285.时晴时雨
Description
Taring喜欢晴天,也喜欢雨天。Taring说:我想体验连续的K天的晴朗,去远足,去放歌;我还想再这K个晴天之后,再去体验连续的K天的云雨,去感受落雨时的轻语。这是令Taring最开心的事情了。其它的时间,Taring会在机房默默的编写着代码。
当然,Taring不想在这连续的K个晴天和连续的K个雨天里被机房的事务打扰或者被自然天气的变化中断。也就是说,这K个晴天和K个雨天必须是连续的,但是他们之间并不需要时间连续。显然的,Taring如果在感受了连续的K天晴朗后,前去机房写代码,等待下雨的时候再次开始他的感悟,这样是不会影响Taring的心情的。
Taring通过天气预报得到了最近连续N天中若干天的天气情况(天气情况只有“晴”和“雨”两种),但其他的天数的天气情况均是未知的。
他想知道有多少种可能的不同的天气情况(对于两种天气情况,如果有任意一天的天气情况是不一样的,就算做不同的天气情况),使他能够完成这两项体验。
InputFormat
输入共有两行。第1行有2个整数,分别表示上文中所述的N和K。
第2行是一个长度为N的字符串,仅包含有B(清),W(雨),X(未知)三种字符。
OutputFormat
输出一个整数Ans,表示合法的天气方案数量。由于Ans可能很大,请将答案Mod1,000,000,007(109+7)
SampleInput1
42 XXXX
SampleOutput1
1
SampleInput2
102 XXBXXWXXXX
SampleOutput2
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.1c[0]=0
2.2c[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>
usingnamespacestd;
constintMaxN=(int)1e6+5;
constintMod=(int)1e9+7;
typedefunsignedlonglongULL;
charS[MaxN];
ULLb[MaxN],c[MaxN],w[MaxN],continous_b[MaxN],total[MaxN];
//continous_b记录的是到i位置时恰好出现了连续的k个B的情况数目不用也行
intmain(intargc,charconst*argv[])
{
intn,k;cin>>n>>k;
S[0]='0';//为了在计算b的时候临界情况的判断
cin>>(S+1);//为了下标对齐,从1开始输入
//求b[]b[i]表示的是从1开始到i位置没有出现过连续的k个B的情况的个数
b[0]=1;
for(inti=1;i<=n;++i)
{
//如果是X由于可以填2种所以乘2注意:此时的b[i]是有badfilling的
//这个badfilling就是指,前i个位置有连续k个B
//假设之前的b全是正确的那么这个badfilling只有一种情况那就是刚好填了B且满足前k个都是B
b[i]=(S[i]=='X')?2*b[i-1]%Mod:1*b[i-1];
continous_b[0]=0;
//为了找出是否此时出现了badfilling我们要记录到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;//可以下一次循环了因为肯定不是badfilling
}
//判断是否出现了badfilling
if(continous_b[i]>=kandS[i-k]!='B'){//正好出现了连续的k个B
//此处右边的1*b[i-k-1]中的1表示的是WBB的情况:只有一种,b[i-k-1]表示的是前i-k-1中没有连续k个B的情况
//#####重点:两者相乘就是所谓的badfillings
intbad;
if(i>k){
bad=1*b[i-(k+1)];
}elseif(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(inti=1;i<=n;++i)
//{
//if(continous_b[i]>=kandS[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';
intcontinous_w=0;
for(inti=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;//不用判断是否有badfullings了
}
if(continous_w>=kandS[i+k]!='W')
w[i]=(w[i]-w[i+k+1]+Mod)%Mod;
}
//记录全部情况的数量
total[n+1]=1;
for(inti=n;i>=0;--i){
total[i]=(S[i]=='X')?2*total[i+1]%Mod:total[i+1];
}
//变换为补集
for(inti=1;i<=n;++i)
w[i]=(total[i]-w[i]+Mod)%Mod;
ULLans=0;
for(inti=1;i<=n-1;++i){
ans=(ans+c[i]*w[i+1])%Mod;
}
cout<<ans<<endl;
//for(inti=1;i<=n;++i)
//{
//cout<<b[i]<<"";
//}cout<<endl;
//for(inti=1;i<=n;++i)
//{
//cout<<c[i]<<"";
//}cout<<endl;
//for(inti=1;i<=n;++i)
//{
//cout<<w[i]<<"";
//}cout<<endl;
return0;
}
/*
102 XXBXXWXXXX32
*/
相关文章推荐
- 【算法学习笔记】37.区间合并问题 SJTU OJ 1262 Milking Cow
- 【算法学习笔记】38.最短路问题 SJTU OJ 1105 path
- 【算法学习笔记】43.动态规划 逆向思维 SJTU OJ 1012 增长率问题
- 【算法学习笔记】51. 贪心法 区间排序问题 SJTU OJ 1360 偶像丁姐的烦恼
- 【算法学习笔记】54.约瑟夫问题 模拟、逆推动规 SJTU OJ 1038 二哥的约瑟夫
- 【算法学习笔记】56.区间合并问题变形 SJTU OJ 1365 最少道路
- 【算法学习笔记】62.状态压缩 DP SJTU OJ 1088 邮递员小F
- 【算法学习笔记】67.状态压缩 DP SJTU OJ 1383 畅畅的牙签袋
- 【算法学习笔记】74. 枚举 状态压缩 填充方案 SJTU OJ 1391 畅畅的牙签袋(改)
- 【算法学习笔记】85.破环为链 序列DP 松弛+代价 SJTU OJ 1073 能量项链
- 【算法学习笔记】58.桶记录 + 前缀和优化 SJTU OJ 1371 期末打分
- 【算法学习笔记】59.链表 SJTU OJ 1368 丁姐的猴子
- 【算法学习笔记】60.经典动态规划 SJTU OJ 1370 赫萝的桃子
- 【算法学习笔记】61.回溯法 DFS SJTU OJ 1106 sudoku
- 【算法学习笔记】63. BFS SJTU OJ 1281 蹦蹦跳跳
- 【算法学习笔记】64. 枚举法 SJTU OJ 1381 畅畅的牙签
- 【算法学习笔记】65. 双向扫描 SJTU OJ 1382 畅畅的牙签盒
- 【算法学习笔记】66. 模拟法 数组链表 报数优化 SJTU OJ 4010 谁最有耐心
- 【算法学习笔记】68.枚举 SJTU OJ 1272 写数游戏
- 【算法学习笔记】69. 枚举法 字典序处理 SJTU OJ 1047 The Clocks