タイトル:クリックして
タイトルの意味:行列の長さは、n個の幅、メートルを考えると、塗りつぶされた長方形のタイル1 * 2 Qは、どのように多くのフィル方法。
1:輪郭DP
範囲は非常に小さく、アウトラインDPが行うことができます。
K5 | K4 | K3 | ||
---|---|---|---|---|
K2 | K1 |
状態を書き換えるk5k4k3k2k1として、彼は1グリッドは、0手段がない場所に置かれていることを示していると述べました。(ステータスコードは、その順番を示す)
、輪郭線とは何ですか?
赤い線は、図中のエッジライン、それによって状態遷移です。:我々は、我々は左から右へ下へ、から、左上隅の位置を各格子状態の分析を検討し、各グリッドは3例あり
A:長方形ホールド
B:横向きに置く
エンド上:Cを
A :保留にし、確かにそれは現時点では満たしていない、の利点を取られているステータスボックス1の上にあるとき、状態は次の行がそれを埋めることができない影響を与えますが、トップが満たされなければなりません。
B:私は横向き、確かではない最初の列を配置する必要があり、その後、私は、グリッドの状態の正面がゼロになると、格子状態は、0になる前に、最後に私の優先順位を埋めるために、グリッドまたは使用の上でなければならない、私は行く必要があります満たされました。
Cは:最後に、確かではない最初の行は、グリッドは、グリッドの上部がゼロになると状態は、まだグリッドのフロントの背面を埋めるためにチャンスを持っているので、私は関係なく、フロントグリッドの、入力する必要があり、0の状態を必要とします。
我々は埋めるかの要件を満たすためによると、そのような転送の状態。
DPは、[i] [j]は[ k]は: 数kが、転送が正常に、すなわち0いっぱいに入れていないときに、i番目の行、j番目の輪郭グリッド方式のステータスを示します。
状態圧延アレイによって生成された輪郭線に関連する唯一の状態の両方。
特定のコードで参照してください。
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
const int NUM=(1<<12);
ll dp[2][NUM];
ll n,m;
int main()
{
ll i,j,k;
while(~scanf("%lld %lld",&n,&m)&&(n||m))
{
if(m>n)//做一点小优化
swap(n,m);
memset(dp,0,sizeof(dp));
dp[0][(1<<m)-1]=1;//开始把上面都看作填满的状态就一种方法
int hh=0;
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
memset(dp[(hh+1)&1],0,sizeof(dp[(hh+1)&1]));
for(k=0;k<(1<<m);k++)
{
if(k&(1<<(m-1)))//不放
{
dp[(hh+1)&1][(k<<1)&((1<<m)-1)]+=dp[hh&1][k];
}
if(j&&!(k&1)&&(k&(1<<(m-1))))//横着放
{
dp[(hh+1)&1][((k<<1)|3)&((1<<m)-1)]+=dp[hh&1][k];
}
if(i&&!(k&(1<<(m-1))))//竖着放
{
dp[(hh+1)&1][((k<<1)|1)&((1<<m)-1)]+=dp[hh&1][k];
}
}
hh++;
}
}
printf("%lld\n",dp[hh&1][(1<<m)-1]);
}
return 0;
}
2:圧力DPと同様
とDP DPは回線状態によってラインの状態の直接の決意であり、DPは格子点からのプロファイルである別のプロファイルです。
私はいっぱいするつもりですので、層状態0がある場合に分析することができ、その後、次の層は、マッチング0 1話を持っている必要があり、この行は次の行に補うためにチャンスがありません埋める場合ではありません。
次の1 0だから最後に実際にあります。
しかし、ここで私たちは、次のグリッドが1であるならば、連続区間1の長さのために、次のグリッドが資格を偶数にする必要があり、グリッド上の1にある場合、以下のグリッドが0または1であることができるか、を判断する必要があります、水平に配置されるすべて、および操作についての判断がです。
特定のコードで参照してください。(状態が実際Oに直接配列を持つテーブルを打つことができるか否かを判断(1)が満たされ、それは少し速く、怠惰少し直接内部に追加されます...それは、制限データはなりません)
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
const int NUM=(1<<12);
ll dp[2][NUM];
ll n,m;
bool check(ll temp)
{
ll cnt=0;
while(temp)
{
if(temp&1)
{
cnt++;
}
else
{
if(cnt&1)
return false;
else
cnt=0;
}
temp>>=1;
}
if(cnt&1)
return false;
else
return true;
}
bool check1(ll x,ll y)
{
for(ll i=0;i<m;i++)
{
if(((1<<i)&y)==0)
{
if((1<<i)&x)
{
continue;
}
else
return false;
}
}
return true;
}
int main()
{
ll i,j,k;
while(~scanf("%lld %lld",&n,&m)&&(n||m))
{
if(m>n)//稍微优化一下
swap(n,m);
memset(dp,0,sizeof(dp));
for(i=0;i<(1<<m);i++)
{
if(check(i))
dp[0][i]=1;
else
dp[0][i]=0;
}
int hh=0;
for(i=1;i<n;i++)
{
memset(dp[(hh+1)&1],0,sizeof(dp[(hh+1)&1]));
for(j=0;j<(1<<m);j++)//这一层的状态
{
for(k=0;k<(1<<m);k++)//上一层的状态
{
if(dp[hh&1][k]==0)
continue;
if(check(j&k)&&check1(j,k))
{
dp[(hh+1)&1][j]+=dp[hh&1][k];
}
}
}
hh++;
}
printf("%lld\n",dp[hh&1][(1<<m)-1]);
}
return 0;
}