Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 12395 | Accepted: 7233 |
Description
Input
Output
Sample Input
1 2 1 3 1 4 2 2 2 3 2 4 2 11 4 11 0 0
Sample Output
1 0 1 2 3 5 144 51205
经典的棋盘覆盖问题。横放看做(11)竖放看做(01),然后按行状压dp。
未优化题解:题解
优化题解:题解
优化是用了dfs去找出第i-1行状态s与第i行哪些状态scur相匹配,而并不是盲目枚举所有状态。这题要理解dfs是有些困难的,再发一篇题解:题解 ,这份题解里的dfs和上面优化代码是类似的,dfs里都考虑了3种情况,
情况一:第i-1行第j列为0,那么第i行第j列只能为1
情况二:第i-1行第j列为1,第i行第j列为0
那么第三种情况有人可能会认为是:第i-1行第j列为1,第i行第j列为1,显然并不是这么简单,比如这种情况
0 1
1 1
这个最后一列1是和1搭配,但是前一列显然对应了竖着放,而最后一列不可能是竖着放,只可能是最后一行横着放,那么就矛盾了,因此如果简单的<第i-1行第j列为1,第i行第j列为1>,dfs往下就会出错,因此可以发现,第三种情况应为:第i-1行第j-1,j列都为1,第i行第j-1,j列都为1。至于第二种情况,你可能怀疑会不会也出现类似第三种情况这种矛盾呢?但是枚举状态后是不会发生的,因此也就是这三种情况了。
优化代码基本还是在状压dp,dfs是对一个特定的状态s进行匹配,而我发的最后一份题解,则换了个思路,是用dfs将所有可能的两行状态全部枚举出来,然后就相当于有了一幅状态转移图,并且对第0行置为全1,这样做的好处是不干扰第一行的放置,因为只有前一行有0,才会影响后一行,这种做法真是太巧妙了。至于后者的做法,令人震惊,他可以解决第一种方法无法解答问题,也就是poj3420.
这个见我的另外一篇博客,传送门:快速幂。
优化的状压dp代码:
#include
#include
#include
#define ll long long
using namespace std;
ll dp[1<<11][11];
int n,m;
bool check(int x){ //检查第一行合法状态
while(x){
if((x&1)&&(x&2)) x>>=2; //出现11
else if(!(x&1))x>>=1; //出现0
else return false; //出现01
}
return true;
}
void dfs(int r,int c,int s,int scur){ //搜索r-1行状态s可以r行匹配的所有状态scur
if(c>m) return;
if(c==m) {dp[scur][r]+=dp[s][r-1];return;}
if(s>>c&1){
dfs(r,c+1,s,scur); //1->0
if(s>>c&2)
dfs(r,c+2,s,scur|3<11
}
else dfs(r,c+1,s,scur|1<1
}
int main()
{
while(cin>>n>>m,n){
if(n*m&1) {puts("0");continue;}
if(m>n) swap(n,m);
memset(dp,0,sizeof dp);
for(int s=0;s<1<
#include
#include
#include
#define ll long long
#define Maxn 2048
using namespace std;
int chg[Maxn*Maxn][2];
ll dp[1<<13][13];
int n,m,tot;
void dfs(int c,int s,int scur){
if(c>m) return;
if(c==m){
chg[tot][0]=s;
chg[tot++][1]=scur;
return;
}
dfs(c+1,s<<1,scur<<1|1); //0->1
dfs(c+1,s<<1|1,scur<<1); //1->0
dfs(c+2,s<<2|3,scur<<2|3); //11->11
}
int main()
{
while(cin>>n>>m,n){
if(m>n) swap(n,m);
tot=0;
dfs(0,0,0);
memset(dp,0,sizeof dp);
dp[(1<