您的位置:首页 > 其它

AtCoder Grand Contest 013D: Piling Up 题解

2017-10-03 23:14 459 查看
非常难的dp

容易想到状态dp[i][j]表示当前进行了i轮操作,第i轮操作结束后盒子里有j个红球的方案数,因为盒子里始终有N个球,所以蓝球的个数为N-j,于是可以转移

但这样有些状态会重复计数
例如:设N=2,则开始状态是Red=2,Blue=0时,可以获得“取出序列”Red,Blue,Red,Blue...
当开始状态是Red=1,Blue=1时,也可以获得“取出序列”Red,Blue,Red,Blue...
我们发现:同一个“取出序列”可以对应多种初始状态,所以有些“取出序列”会被重复计算
一个很好的比喻是,如果将这个“取出序列”在坐标系中化成一条折线,横坐标表示取到第几轮,纵坐标表示盒子里有几个红球
那么同样形状的折线对应的应该是同一种“取出序列”,然而由于折线的出发点不同,即刚开始红球的数量不确定,所以从每个点出发都会有这样一条折线
于是对于同样形状的曲线,我们考虑取其中最特殊的一条:初始状态中红球数最小的一条,即在坐标系中出发点最低的一条
最低的一条应该满足存在一个时刻,红球的数量为0,否则如果红球数量一直大于零,初始状态中的红球一定可以再减少
如下图,两条曲线代表同样的答案被计算多次,我们只计与x轴“相切”的那条



于是有新状态dp[i][j][0/1],第三维为0表示这个序列已经在之前(包括现在)的某时刻红球数为0,第三维为1反之亦然
我们有了正确的状态,但转移也非常难写
一方面要考虑到j=0,j=1,j=n的边界条件
另一方面,红红,红蓝,蓝红,蓝蓝四种转移方法,注意在红蓝和蓝红这两种转移方法中,在转移中间(即拿完红球还没补球时)红球数为0也算与x轴相切

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cstdlib>
#include <utility>
#include <stack>
#include <queue>
#include <deque>
#include <set>
#include <map>
#include <vector>
#include <cmath>
#define Pair pair<int,int>
#define LOWBIT(x) x & (-x)
#define LL long long
#define mp make_pair
#define pb push_back
#define x first
#define y second
using namespace std;

const int MOD=1e9+7;
const int INF=0x7ffffff;
const int magic=348;
const double eps=1e-9;

//ith step,j red
LL dp[3048][3048][2];
int n,m;

inline LL mod(LL x)
{
while (x>=MOD) x-=MOD;
while (x<0) x+=MOD;
return x;
}

int main ()
{
int i,j,k;
scanf("%d%d",&n,&m);
dp[0][0][0]=1;
for (i=1;i<=n;i++)
dp[0][i][1]=1;
LL ans=0;
for (i=1;i<=m;i++)
for (j=0;j<=n;j++)
{
if (j==0)
{
dp[i][j][0]=mod(dp[i-1][1][0]+dp[i-1][1][1]+dp[i-1][0][0]);
if (i==m) ans=mod(ans+dp[i][j][0]);
continue;
}
if (j==1)
{
dp[i][j][0]=mod((j+1<=n?dp[i-1][j+1][0]:0)+(j==n?dp[i-1][j][0]:2*dp[i-1][j][0])+dp[i-1][j-1][0]+dp[i-1][j][1]);
if (i==m) ans=mod(ans+dp[i][j][0]);
dp[i][j][1]=mod((j+1<=n?dp[i-1][j+1][1]:0)+(j==n?0:dp[i-1][j][1]));
continue;
}
dp[i][j][0]=mod((j+1<=n?dp[i-1][j+1][0]:0)+(j==n?dp[i-1][j][0]:2*dp[i-1][j][0])+dp[i-1][j-1][0]);
dp[i][j][1]=mod((j+1<=n?dp[i-1][j+1][1]:0)+(j==n?dp[i-1][j][1]:2*dp[i-1][j][1])+dp[i-1][j-1][1]);
if (i==m) ans=mod(ans+dp[i][j][0]);
}
printf("%I64d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: