关于插头dp的一点理解
如果是自学的话,有福了,我懵逼了好几天
但可以看看这个PPT
在看看代码应该可以加深理解。练习题一,我写了一点注释,希望有帮助
前导算法
状态压缩dp
位运算
算法干嘛
求哈密顿回路 条数
算法思路
定义了两个东西 (PPT中图文相结合)
1.插头:一个格子某个方向的插头存在表示这个格子在这个方向与相邻格子相连.
2.轮廓线:已决策格子和未决策格子的分界线
我们用状态压缩的方法 表示一排插头的状态
状态转移和PPT略有不同,我们采用2进制,方便位运算。共有三类
1.右下没有插头:如果经过这个格子,左上插头
2.右下有两个插头:左上不能有插头了
3.右下有一个插头:左上还应该有一个插头
算法阶段
第1步 整体的初始化
第2步 枚举行(调用3 4 5)
第3步 对本行初始化(传承 本行 上一行)
第4步 枚举元素 (调用 5)
第5步 枚举状态 ,状态转移
个人感觉
好牛叉的算法,针对性似乎很强
模板
memset(dp,0,sizeof(dp));
dp[0][m][0]=1;
for(int i=1;i<=n;i++){ //枚举每一行
for(int k=0;k<(1<<m);k++){ //初始化本行第零个元素 即加入第一个向右插头
dp[i][0][k<<1]=dp[i-1][m][k];
}
for(int j=1;j<=m;j++){ //枚举本行的每个元素
int r=1<<j; // 用于检查向右的插头
int d=1<<(j-1); // 用于检查向下的插头
for(int k=0;k<(1<<m+1);k++){ //枚举可以的状态
if(mp[i][j]){ //有树要吃
if((r&k)&&(d&k)){ //有两个插头
dp[i][j][k]=dp[i][j-1][k-r-d]; // 没有插头传递过来
}
else if((r&k)==0&&(d&k)==0){ // 没有插头
dp[i][j][k]=dp[i][j-1][k+r+d]; //说明 有两个插头传递过来
}
else{ //有一个插头
dp[i][j][k]=dp[i][j-1][k^r^d]+dp[i][j-1][k]; // 传递一个插头
}
}
else{//没有树
if((r&k)==0&&(d&k)==0){ //没有插头
dp[i][j][k]=dp[i][j-1][k]; //无需传递插头
}
else{
dp[i][j][k]=0; // 不可能有插头
}
}
}
}
}
这些题大家可以练习(遇到补充)
1.HDU - 1693 Eat the Trees
求哈密顿回路数,
我写的题解