[jzoj]1262. 为奶牛熄灯(迭代加深搜索+一堆优化vs记忆化vs状压DP)
来源:互联网 发布:戴予桐 直播软件 编辑:程序博客网 时间:2024/06/03 17:44
Description
奶牛们喜欢在黑暗的环境里睡觉。当她们每晚回到牛棚准备睡觉时,牛棚里有L(3<=L<=50)盏灯仍然亮着。所有灯的开关按编号升序排成一列,最左边的那个开关控制1号灯(所谓控制,也就是如果1号灯现在亮着,那么按这个开关会使1号灯熄灭,否则这个操作会使1号灯被点亮)。由于奶牛们的蹄子过于粗大,没法方便地按开关,她们总是用一个特制的干草叉来进行对开关的操作。这个叉子设计了T(1<=T<=7)个叉尖,相邻叉尖的距离正好与相邻开关的距离相等。但是现在有些叉尖被折断了。比如说,T=4的一个干草叉,它的第3根叉尖被折断了,我们就用’1101’来描述它。
如果把这个叉子的最左端对准那一列开关的最左端,按下,那1号、2号和4号灯的状态会被改变(3号灯的状态不变,因为那个叉尖被折断了)。在进行这样的操作的时候,任何一个叉尖都必须有一个对应的开关,也就是说,叉子的边缘不能在那一列开关的范围外,即使边缘处的叉尖已经被折断。
现在,你已经知道了各个灯的状态,以及干草叉现在的情况,请你找出一个操作序列,使得在所有操作完成之后,仍然亮着的灯的数目最少。
Input
第1行: 两个用空格隔开的整数:L 和 T
第2行: 一个长度为L的字符串,串中不含空格且元素均为’0’或’1’。第i个元素是’1’则表示第i盏灯亮着,是’0’的话就表示第i盏灯已经被关掉
第3行: 一个长度为T的字符串,只含’0’或’1’(同样不含空格)。如果第i个元素是’1’,说明干草叉的第i根叉尖仍完好无损,否则说明第i根叉尖已经被折断
Output
第1行: 输出一个正整数K,即为了达到目的一共需要用叉子按多少次开关
Sample Input
10 4
1111111111
1101
Sample Output
5
Data Constraint
Hint
【样例说明】
所有的10盏灯都开着。奶牛们使用的干草叉有4个齿,其中第3个齿已经被折断了。
1111111111 开始
1100101111 操作 3(即为把叉子的第一个齿对准第3个开关,按下)
0001101111 操作 1
0000000111 操作 4
0000001010 操作 7
0000010000 操作 6
最后,有1盏灯仍然亮着。这是借助这个干草叉所能得到的最佳结果。当然,可行操作还有很多(最后剩下的灯可能不同)。
Preface
因为
但因为我考场上时脑抽打了个DFS,后来看题解说dfs也能过,于是,我就下定决心,要把这题用搜索切了,再打dp,于是就有了下面这些东西.
Solution
搜索
Method1.0
考虑用
Method1.5
在Method1.0上,我们知道操作是可逆的,即无论操作顺序,最终得到的状态是一样的.
所以我们只考虑对于每一个位置是否进行操作.
用一个字符串储存状态,每次暴力更新,时间复杂度
#include <iostream>#include <cstring>#include <cstdio>using namespace std;char st[52],ch[52];int L,T,i,tot,mmin,ans;void dfs(int k,int sum,int total,int bz){ if (sum<mmin) { mmin=sum; ans=total; } if (sum==mmin) ans=min(ans,total); if (k>L-T+1) return; tot=0; for (int j=1;j<=T;j++) if (ch[j]=='1'){ if (st[j+k-1]=='1') { st[j+k-1]='0'; tot--; } else { st[j+k-1]='1'; tot++; } } dfs(k+1,sum+tot,total+1,1); for (int j=1;j<=T;j++) if (ch[j]=='1'){ if (st[j+k-1]=='1') st[j+k-1]='0'; else st[j+k-1]='1'; } dfs(k+1,sum,total,0);}int main(){ freopen("xlite.in","r",stdin); freopen("xlite.out","w",stdout); scanf("%d%d",&L,&T); scanf("%s",st+1); scanf("%s",ch+1); tot=0; for (i=1;i<=L;i++) if (st[i]=='1') tot++; mmin=1e8; ans=1e8; dfs(1,tot,0,1); printf("%d\n",ans);}
Method1.5
发现上面这个cx打的很不优美
因为是选和不选,最后统计1的个数其实是一样的,不需要每次都统计,这样常数会大.
因为没有加剪枝,所以这样每次统计的效率反而低了.
#include <iostream>#include <cstring>#include <cstdio>using namespace std;char st[52],ch[52];int L,T,i,tot,mmin,ans,sum;void dfs(int k,int total,int bz){ if (k>L-T+1) { sum=0; for (int j=1;j<=L;j++) if (st[j]=='1') sum++; if (sum<mmin) { mmin=sum; ans=total; } if (sum==mmin) ans=min(ans,total); return; } tot=0; for (int j=1;j<=T;j++) if (ch[j]=='1'){ if (st[j+k-1]=='1') st[j+k-1]='0'; else st[j+k-1]='1'; } dfs(k+1,total+1,1); for (int j=1;j<=T;j++) if (ch[j]=='1'){ if (st[j+k-1]=='1') st[j+k-1]='0'; else st[j+k-1]='1'; } dfs(k+1,total,0);}int main(){ freopen("xlite.in","r",stdin); freopen("xlite.out","w",stdout); scanf("%d%d",&L,&T); scanf("%s",st+1); scanf("%s",ch+1); mmin=1e8; ans=1e8; dfs(1,0,1); printf("%d\n",ans);}
Method2.0(错误方法)
考场上时,想了个不靠谱的记忆化.
设
这个记忆化分明是对的呀?因为前
但是智障的在于,我的
因为刚打C++,不会random(尬),自己手出的数据点错了,于是就加多了一维表示第
最后还是尬,这一题也告诉了我对拍是有多么重要.
虽然最后还是多水了一个点.
附:Wrongans三个点,T了三个点.
Method2.5(记忆化)
那如果我处理的是前
会出现怎样的情况?
还是会WA?
为什么?
原来,上面所说的“这个记忆化分明是对的呀?因为前
还是有影响的, 且影响有
这样就可以记忆化了.
#include <iostream>#include <cstring>#include <cstdio>using namespace std;char st[52],ch[52];int L,T,i,mmin,ans,sum,tot,have;short int f[51][129][51];void dfs(int k,int total,int rec){ if (k>L-T+1) { sum=0; for (int j=1;j<=L;j++) if (st[j]=='1') sum++; if (sum<mmin) { mmin=sum; ans=total; } if (sum==mmin) ans=min(ans,total); return; } if (k>1) { have=0; for (int j=k+T-2;j>=k-1;j--) if (st[j]=='1') have=have*2+1; else have=have*2; if (total<f[k-1][have][rec]) f[k-1][have][rec]=total; else return; } for (int j=1;j<=T;j++) if (ch[j]=='1'){ if (st[j+k-1]=='1') st[j+k-1]='0'; else st[j+k-1]='1'; } tot=0; if (st[k]=='1') tot++; dfs(k+1,total+1,rec+tot); for (int j=1;j<=T;j++) if (ch[j]=='1'){ if (st[j+k-1]=='1') st[j+k-1]='0'; else st[j+k-1]='1'; } tot=0; if (st[k]=='1') tot++; dfs(k+1,total,rec+tot);}int main(){ freopen("xlite.in","r",stdin); freopen("xlite.out","w",stdout); scanf("%d%d",&L,&T); scanf("%s",st+1); scanf("%s",ch+1); mmin=1e8; ans=1e8; memset(f,30,sizeof(f)); dfs(1,0,0); printf("%d\n",ans);}
Method3.0
我们加上一些非常容易想到的剪枝,即如果当前无法改变的‘1’的个数已经大于最优解了,直接return.
并且我们不需要在更新最优解时统计1的个数,我们可以边算边统计.
#include <iostream>#include <cstring>#include <cstdio>using namespace std;char st[52],ch[52];int L,T,i,mmin,ans,sum,tot,have;short int f[51][129][51];void dfs(int k,int total,int rec){ if (rec>mmin) return; if ((rec==mmin)&&(total>ans)) return; if (k>L) { if (rec<mmin) { mmin=rec; ans=total; } if (rec==mmin) ans=min(ans,total); return; } if (k<=L-T+1) { if (k>1) { have=0; for (int j=k+T-2;j>=k-1;j--) if (st[j]=='1') have=have*2+1; else have=have*2; if (total<f[k-1][have][rec]) f[k-1][have][rec]=total; else return; } for (int j=1;j<=T;j++) if (ch[j]=='1'){ if (st[j+k-1]=='1') st[j+k-1]='0'; else st[j+k-1]='1'; } if (st[k]=='1') tot=1; else tot=0; dfs(k+1,total+1,rec+tot); for (int j=1;j<=T;j++) if (ch[j]=='1'){ if (st[j+k-1]=='1') st[j+k-1]='0'; else st[j+k-1]='1'; } } if (st[k]=='1') tot=1; else tot=0; dfs(k+1,total,rec+tot);}int main(){ freopen("xlite.in","r",stdin); freopen("xlite.out","w",stdout); scanf("%d%d",&L,&T); scanf("%s",st+1); scanf("%s",ch+1); mmin=1e8; ans=1e8; memset(f,30,sizeof(f)); dfs(1,0,0); printf("%d\n",ans);}
Method4.0
运用迭代加深搜索的思想,我们限定一个‘1’个数的上限,如果超过了直接return,这样在最优解总‘1’个数较少的情况下,这种方法的优化程度较大.
但因为这里已经加了记忆化,所以很多不必要的操作已经return了,然后再枚举‘1’的个数,反而有点多余,但是这毕竟是一种思路,一种很牛逼的思路.
新套路get.
#include <iostream>#include <cstring>#include <cstdio>using namespace std;char st[52],ch[52];int L,T,i,mmin,ans,sum,tot,have,tottot;short int f[51][129][51];void dfs(int k,int total,int rec){ if (rec>mmin) return; if ((rec==mmin)&&(total>ans)) return; if (k>L) { if (rec==mmin) ans=min(ans,total); return; } if (k<=L-T+1) { if (k>1) { have=0; for (int j=k+T-2;j>=k-1;j--) if (st[j]=='1') have=have*2+1; else have=have*2; if (total<f[k-1][have][rec]) f[k-1][have][rec]=total; else return; } for (int j=1;j<=T;j++) if (ch[j]=='1'){ if (st[j+k-1]=='1') st[j+k-1]='0'; else st[j+k-1]='1'; } if (st[k]=='1') tot=1; else tot=0; dfs(k+1,total+1,rec+tot); for (int j=1;j<=T;j++) if (ch[j]=='1'){ if (st[j+k-1]=='1') st[j+k-1]='0'; else st[j+k-1]='1'; } } if (st[k]=='1') tot=1; else tot=0; dfs(k+1,total,rec+tot);}int main(){ freopen("xlite.in","r",stdin); freopen("xlite.out","w",stdout); scanf("%d%d",&L,&T); scanf("%s",st+1); scanf("%s",ch+1); tottot=0; for (i=1;i<=L;i++) if (st[i]=='1') tottot++; for (mmin=0;mmin<=tottot;mmin++) { memset(f,30,sizeof(f)); ans=1e8; dfs(1,0,0); if (ans<1e8) break; } printf("%d\n",ans);}
考场上时打的记忆化,是为了节省时间去做后边的题,数据范围不大,记忆化有时并不比dp慢,但因为长年打DP,以至于记忆化都生疏了,其实记忆化也是DP的思想,如果连记忆化都不会,还打什么DP.
状压DP
此题还可运用状压DP求解.
转化一下思路,与记忆化不能完全一样,否则无法转移.
设
这样设就可以从
此题的一些小技巧
其实我们的dfs还可以用位运算优化,但是由于我加了记忆化,所以位运算起不到什么用处.
但是还是有思想可以借鉴的.
如我要算出前127个数中每一个数转成2进制出现1的个数.
可以直接通过
for (int i=1;i<=127;i++) f[i]=f[i>>1]+(i&&1)
得出.
不需要再去暴力或者dfs之类的了.
- [jzoj]1262. 为奶牛熄灯(迭代加深搜索+一堆优化vs记忆化vs状压DP)
- code[vs] 1004 四子连棋(迭代加深搜索)
- jzoj 1262 为奶牛熄灯
- 奶牛求幂 迭代加深搜索
- [迭代加深 A *] Palamaze (皇宫VS迷宫)
- code vs 1288 埃及分数 (迭代加深搜)
- 迭代加深搜索 + dp vijos 1159
- 迭代加深搜索
- 迭代加深搜索
- 搜索-迭代加深
- 奶牛求幂(迭代加深经典减枝)
- HDU3920 状压DP+优化+记忆化搜索
- 埃及分数(迭代加深搜索)
- 埃及分数(迭代加深搜索)
- 埃及分数(迭代加深搜索)
- 迭代加深搜索(埃及分数)
- uva12558 (迭代加深搜索)
- 棋盘染色(迭代加深搜索)
- 主从DB与cache一致性
- 浅析Python3中的bytes和str类型
- 3.Actitity的生命周期
- [cv] optical flow
- java常用类:List接口常用方法
- [jzoj]1262. 为奶牛熄灯(迭代加深搜索+一堆优化vs记忆化vs状压DP)
- Redis脚本
- sdut-1117 C语言实验——求绝对值(选择结构)
- Error:此运算符的参数太多
- 【Luogu P1048 Luogu P1016】采药/疯狂的采药
- JAVA设计模式--单例模式
- python定向爬虫——爬取某代理Ip网站上的所有ip
- paly 框架Cannot load this JVM TI agent twice, check your java command line for duplicate jdwp options.
- Ext JS 6:将日期字段修改为日期时间字段(二)