标签:蒙德里安 状压 acwing291 k5 轮廓线 赋为 include 方块
我蒙德里安有一个梦想,就是成为方块填充之王!
POJ地址//HDU地址//acwing地址
(!注意,学习此方法需要一定状压基础,请至少做两道状压\(DP\)再学习)
一开始我脑中蹦出了状压整张图的想法,但显然不行,这时想一下,其实我们在乎的方格并不多,其实只需要一行
但是方块又有两种摆放方法,不能只存上一行,还需要知道左边的方格的信息,于是有大佬就发明了这个轮廓线做法
如图,\(x\)为\(dp\)过程中枚举的转移点,虚线即为轮廓线,我们状压轮廓线下的所有方块是否被覆盖
我们可以在\(k5=0\)时放置竖着的方块,在\(k5!=0\)时不放方块或在\(k0=0\)和\(k5!=0\)同时成立时在\(x\)处放置横向的方块(整张图必须填满)
如此甚好,然后我们便可以将轮廓往右推,如图
这样便能在下次继续转移,那么如何实现右移?
首先将整个状态\(k\)右移(即k<<=1),然后将第一位赋为\(1\)(当\(x\)上放置了方块时)
对于放置竖着的方块,我们将\(k5\)赋为\(0\)(因为已经溢出了,不予考虑)
对于横着的方块我们将\(k0\)(第二位)赋为\(1\)
若我们没有放置方块,那么此时\(k5\)时有值的,需要和情况一样赋为\(0\)
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
ll f[3][100000],n,m,mst,now,old;
int main(){
scanf("%lld%lld",&n,&m);
while(n&&m){
mst=(1<<m)-1;
now=0,old=1;
memset(f,0,sizeof(f));
f[now][mst]=1;
for(ll i=1;i<=n;++i){
for(ll j=1;j<=m;++j){
swap(old,now);
memset(f[now],0,sizeof(f[now]));
for(ll k=0;k<=mst;++k){
if(k&(1<<(m-1))){
f[now][(k<<1)&(~(1<<m))]+=f[old][k];
if(j>1&&!(k&1))f[now][((k<<1)|3)&(~(1<<m))]+=f[old][k];
}
if(i>1&&!(k&(1<<(m-1))))f[now][(k<<1)^1]+=f[old][k];
}
}
}
printf("%lld\n",f[now][mst]);
scanf("%lld%lld",&n,&m);
}
}
标签:蒙德里安,状压,acwing291,k5,轮廓线,赋为,include,方块 来源: https://www.cnblogs.com/caijiLYC/p/14358687.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。