题目链接
题意:给你n根火柴棒(5<=n<=500),问这些火柴棒能拼成多少种形如A-B=C的等式(不包含前导0)。
HRBUST - 1118的强化版,那个直接搜索就能过,这个只能dp。
分析:首先去掉3根火柴棒来凑成减号和等号,然后问题就变成了将剩下的火柴棒拼成B+C=A的形式。
我们观察能凑成的等式中三个数字的相同位置,发现无非就以下几种情况:
其中,从低位向高位转移的可行方案有:
(1)(2)->(1)(3)(4)(不进位),(2)(5)(6)(7)(进位)
(3)->(3)
(4)->(4)
(5)->(3)(不进位),(5)(7)(进位)
(6)->(4)(不进位),(6)(7)(进位)
看上去似乎有点复杂。为了直观,我们用d[n][a][b][c]来表示剩余n根火柴棒时,从最低位开始的放法总数,a表示最低位是否因进位需要+1,b表示是否已停止放B位,c表示是否已停止放C位,则最终我们所求的结果就是d[n-3][0][0][0]。
#define FRER() freopen("i.txt","r",stdin)
#define FREW() freopen("o.txt","w",stdout)
#include
using namespace std;
typedef long long ll;
const int N=500+10;
const int num[]= {6,2,5,5,4,5,6,3,7,6};
int n,kase=0;
ll d[N][2][2][2],mod;
ll dp(int cnt,int a,int b,int c)
{
if(cnt<0)return 0;
if(~d[cnt][a][b][c])return d[cnt][a][b][c];
if(b&&c)
{
if((a&&cnt==num[1])||(!a&&!cnt))return 1;
else return 0;
}
d[cnt][a][b][c]=0;
if(!b&&!c)
{
for(int i=0; i<=9; ++i)
for(int j=0; j<=9; ++j)
{
d[cnt][a][b][c]+=dp(cnt-(num[i]+num[j]+num[(i+j+a)%10]),(i+j+a)/10,0,0);
if(i)d[cnt][a][b][c]+=dp(cnt-(num[i]+num[j]+num[(i+j+a)%10]),(i+j+a)/10,1,0);
if(j)d[cnt][a][b][c]+=dp(cnt-(num[i]+num[j]+num[(i+j+a)%10]),(i+j+a)/10,0,1);
if(i&&j)d[cnt][a][b][c]+=dp(cnt-(num[i]+num[j]+num[(i+j+a)%10]),(i+j+a)/10,1,1);
}
}
else if(!b)
{
for(int i=0; i<=9; ++i)
{
d[cnt][a][b][c]+=dp(cnt-(num[i]+num[(i+a)%10]),(i+a)/10,0,1);
if(i)d[cnt][a][b][c]+=dp(cnt-(num[i]+num[(i+a)%10]),(i+a)/10,1,1);
}
}
else if(!c)
{
for(int i=0; i<=9; ++i)
{
d[cnt][a][b][c]+=dp(cnt-(num[i]+num[(i+a)%10]),(i+a)/10,1,0);
if(i)d[cnt][a][b][c]+=dp(cnt-(num[i]+num[(i+a)%10]),(i+a)/10,1,1);
}
}
return d[cnt][a][b][c]%=mod;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(d,-1,sizeof d);
scanf("%d%I64d",&n,&mod);
printf("Case #%d: %I64d\n",++kase,dp(n-3,0,0,0));
}
return 0;
}