【ssl1383】车2(II)【状压DP】

【ssl1383】车2(II)【状压DP】_第1张图片

Sample Input

3 3 2

Sample Output

24

分析

做状压DP的我心态逐渐爆炸。

首先我们要用DFS枚举每一行的状态。用 a [ j s ] a[js] a[js]表示状态的二进制数, n u m [ j s ] num[js] num[js]表示每行棋子的个数。

void dfs(int ans,int dep,int ff)
{
	if(dep>n)
	{
		js++;
		a[js]=ans;
		num[js]=ff;
		return;
	}
	dfs(ans,dep+1,ff);
	dfs(ans+(1<<(dep-1)),dep+2,ff+1);
}

然后进行DP。
f [ i ] [ a [ j ] ] [ l ] f[i][a[j]][l] f[i][a[j]][l]为第i行a[j]的状态下棋子为l所得的方案数。
可得动态转移方程:
f [ i ] [ a [ j ] ] [ l ] + = f [ i − 1 ] [ a [ w ] ] [ l − n u m [ j ] ] ; f[i][a[j]][l]+=f[i-1][a[w]][l-num[j]]; f[i][a[j]][l]+=f[i1][a[w]][lnum[j]];
DP之后累加所有方案数。

上代码

#include
#include
#include
#include
typedef long long ll;
using namespace std;

int n,m,k,js,ans;
int a[100001],num[100001],c[100001];
int f[82][1<<9][21];

void dfs(int ans,int dep,int ff)//dfs枚举每一行的状态
{
	if(dep>n)//当前行结束,统计
	{
		js++;
		a[js]=ans;
		num[js]=ff;
		return;
	}
	dfs(ans,dep+1,ff);//这个点不放
	dfs(ans+(1<<(dep-1)),dep+2,ff+1);//这个点放
}

int main()
{
    cin>>n>>m>>k;
    if(n<m) swap(n,m);
    dfs(0,1,0);
    for(int i=1;i<=js;i++)//初始化
    {
    	f[1][a[i]][num[i]]=1;
    }
    for(int i=2;i<=m;i++)
    {
    	for(int j=1;j<=js;j++)//枚举上一行状态
    	{
    		for(int w=1;w<=js;w++)//枚举当前行的状态
    		{
    			if(a[j]&a[w])//如果是(1,1)就不能转移,只有(0,0),(0,1),(1,0)可以
    			{
    				continue;
    			}
    			for(int l=0;l<=k;l++)
    			{
    				if(l>=num[j])//不能减出负数
    				{
    					f[i][a[j]][l]+=f[i-1][a[w]][l-num[j]];//根据上一个状态转移过来
    				}
    			}
    		}
    	}
    }
    for(int i=1;i<=js;i++)
    {
    	ans+=f[m][a[i]][k];//累加所有的方案
    }
    cout<<ans;
	return 0;
}

你可能感兴趣的:(DP,DFS,SSL题库,状压DP)