Atcoder Grand Contest 013 D Piling Up

Piling Up

Problem Statement

在箱子里放 n n 个球,有黑白两种颜色。
接下来依次执行 m m 轮操作,每轮操作分成三步:
1、抓箱子里一个球堆在塔顶。
2、往箱子里放入一个黑球和一个白球。
2、再抓箱子里的一个球堆在塔顶。
求不同塔的方案数(两个塔不同当前仅当存在一个位置球的颜色不同)。

Data Constraint

1 1 n n 3000 3000
1 1 m m 3000 3000

Solution

首先一轮操作的前后球的总数一定是 n n
先给出一个错误的 dp d p
fi,x f i , x 表示执行完了前 i i 轮,箱子里黑球的数量为 x x 的,堆出的塔的不同的方案数。
转移的话就是枚举两次拿出的球的颜色的所有可能性然后进行转移。
但这样显然会算重。
考虑如何改进 dp d p 使得它变正确。
考虑一下为什么会算重。
如果从头到尾黑球的数量没有到达过 0 0 ,那么把多余的黑球全部换成白球后,这样的方案堆出来 的塔是一样的,因此这样做会算重。
根据上述内容我们可以得到启发,设 fi,x,0/1 f i , x , 0 / 1 表示执行完了前i轮,箱子里黑球的数量为 x x ,黑球的数 量在前 i i 轮中是否到达过 0 0 ,最后只能选择黑球数量到达过 0 0 f f 统计到答案中去。
这样便能做到不重不漏。

Code

#include
#include
#include
#include

#define fo(i,j,l) for(int i=j;i<=l;++i)
#define fd(i,j,l) for(int i=j;i>=l;--i)

using namespace std;
typedef long long ll;
const ll N=32e2,mo=1e9+7;

ll f[2][N][2];

int n,m;

int main()
{
    cin>>n>>m;
    fo(i,1,n)f[0][i][0]=1;
    f[0][0][1]=1;
    int u=0,v;
    fo(i,1,m){
        v=u^1;
        fo(i,0,n)f[v][i][0]=f[v][i][1]=0;
        fo(i,0,n)f[u][i][0]=f[u][i][0]%mo,f[u][i][1]=f[u][i][1]%mo;
        fo(i,1,n)f[v][i-1][(i==1)]=f[v][i-1][(i==1)]+f[u][i][0],f[v][i-1][1]=f[v][i-1][1]+f[u][i][1];
        fo(i,1,n)f[v][i][(i==1)]=f[v][i][(i==1)]+f[u][i][0],f[v][i][1]=f[v][i][1]+f[u][i][1];
        fo(i,0,n-1)f[v][i][0]=f[v][i][0]+f[u][i][0],f[v][i][1]=f[v][i][1]+f[u][i][1];
        fo(i,0,n-1)f[v][i+1][0]=f[v][i+1][0]+f[u][i][0],f[v][i+1][1]=f[v][i+1][1]+f[u][i][1];
        u=v;
    }
    ll ans=0;
    fo(i,0,n)ans=(ans+f[u][i][1])%mo;
    cout<

你可能感兴趣的:(动态规划)