题意:给一个表达式形如a+b=c,其中一些数字为?,问使表达式成立有多少种情况。
思路:不太容易发现是一道数位DP。比较容易建立状态转移方程,dp[i][j][k][l]表示j(0表示不进位,1表示进位)使a的第i位为k,b的第i位为l的情况个数,这样可以计算得c的第i位。用线性时间枚举位数,10^2枚举过去状态,10^2枚举现在状态建立转移即可。最后累加求和。注意前导零的问题。给一组数据??+??=???,答案是4860。注意如0?此类情况转移的状态是无效的,因此它的状态直接置0即可。
#include <iostream>
#include <string>
#include <cstring>
#include <cctype>
#include <cstdio>
#include <map>
#include <cmath>
#include <stack>
#include <algorithm>
using namespace std;
typedef long long LL ;
LL dp[12][2][12][12];
int main()
{
char s[100];
int kase=0;
while(scanf("%s",s)!=EOF)
{
printf("Case %d: ",++kase);
char a[20],b[20],c[20];
int l=strlen(s);
char *p1=find(s,s+l,'+');
*p1=0;
strcpy(a,s);
char *p2=find(p1+1,s+l,'=');
*p2=0;
strcpy(b,p1+1);
strcpy(c,p2+1);
int la=strlen(a),lb=strlen(b),lc=strlen(c);
reverse(a,a+la);
reverse(b,b+lb);
reverse(c,c+lc);
int p;
for(p=la; p<lc; ++p ) a[p]='0';
a[p]=0;
for(p=lb; p<lc; ++p ) b[p]='0';
b[p]=0;
memset(dp,0,sizeof(dp));
dp[0][0][0][0]=1;
for(int i=1; i<=lc; ++i)
{
for(int ja=0; ja<10; ++ja)
{
for(int jb=0; jb<10; ++jb)
{
for(int ka=0; ka<10; ++ka)
{
if(a[i-1]!='?'&&a[i-1]!=ka+'0') continue;
for(int kb=0; kb<10; ++kb)
{
if(b[i-1]!='?'&&b[i-1]!=kb+'0') continue;
int x,jc,kc,kx;
x=(ja+jb)/10;
jc=(ja+jb+0)%10;
kc=(ka+kb+x)%10;
if(c[i-1]=='?'||(kc+'0'==c[i-1]))
{
if((la!=1&&i==la&&ka==0)||(lb!=1&&i==lb&&kb==0))dp[i][x][ka][kb]=0;
else dp[i][x][ka][kb]+=dp[i-1][0][ja][jb];
}
x=(ja+jb+1)/10;
jc=(ja+jb+1)%10;
kc=(ka+kb+x)%10;
if(c[i-1]=='?'||(kc+'0'==c[i-1]))
{
if((la!=1&&i==la&&ka==0)||(lb!=1&&i==lb&&kb==0))dp[i][x][ka][kb]=0;
else dp[i][x][ka][kb]+=dp[i-1][1][ja][jb];
}
}
}
}
}
}
LL ans=0;
int si=0,sj=0;
if(la==lc&&la!=1) si=1;
if(lb==lc&&lb!=1) sj=1;
for(int i=si; i<=9; ++i)
for(int j=sj; j<=9; ++j)
{
if((i+j+1)/10==0)
{
if((i+j+1)%10!=0||lc==1)
ans+=dp[lc][1][i][j];
}
if((i+j)/10==0)
{
if((i+j)%10!=0||lc==1)
ans+=dp[lc][0][i][j];
}
}
printf("%I64d\n",ans);
}
return 0;
}