您的位置:首页 > 其它

UVa11038 - How Many O's?(数位dp)

2017-10-28 14:34 381 查看
题目链接

简介:

n~m出现过多少次0

分析:

之前做过更变态的

设计状态:

f[i][j][k][0/1][0/1]

表示第i位填的数是j,1~i位出现过k次0(不算前导零),1~i位是不是都是前导零,卡不卡上界

初始化有一点。。额。。

for (i=0;i<=a[1];i++)
f[1][i][0][i==0 ? 1:0][i==a[1] ? 1:0]=1;


第一为不管填的是什么,我们都认为没有0出现,因为我们不计算前导0

转移也有一点麻烦(我把所有情况都分开了,这样优美一点):

if (!l&&c==0)       //出现了有效0
f[i+1][c][k+1][0][s&&c==a[i+1]]+=f[i][j][k][l][s];
else if (l&&c==0)   //全是前导零
f[i+1][c][k][1][s&&c==a[i+1]]+=f[i][j][k][l][s];
else
f[i+1][c][k][0][s&&c==a[i+1]]+=f[i][j][k][l][s];


最后的答案统计:

枚举j,k,s

ans+=f[len][j][k][0][s]*k

最后别忘了把0加上(之前的计算为了方便,我们把0省去了)

//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long

using namespace std;

ll f[11][10][11][2][2];
//f[i][j][k][l][s]   第i位是j,前i位有k个0,前i位是不是前导0,卡不卡上界
ll n,m;
int a[11],b[11];

void cl(ll x,int *a)
{
memset(a,0,sizeof(a));
if (x<0) return;
if (x==0)
{
a[0]=1;
return;
}
while (x)
{
a[0]++;
a[a[0]]=x%10;
x/=10;
}
for (int i=1;i<=a[0]/2;i++) swap(a[i],a[a[0]-i+1]);
}

ll doit(int *a)
{
memset(f,0,sizeof(f));

if (a[0]==1&&a[1]==0) return 1;
int i,j,k,l,s,c;
for (i=0;i<=a[1];i++) f[1][i][0][i==0 ? 1:0][i==a[1] ? 1:0]=1;       //前导零我们不算

for (i=1;i<a[0];i++)
for (j=0;j<=9;j++)
for (k=0;k<=i;k++)
for (l=0;l<=1;l++)
for (s=0;s<=1;s++)
if (f[i][j][k][l][s])
{
int tt=9;
if (s) tt=a[i+1];
for (c=0;c<=tt;c++)
{
if (!l&&c==0)
f[i+1][c][k+1][0][s&&c==a[i+1]]+=f[i][j][k][l][s];
else if (l&&c==0)   //全是前导零
f[i+1][c][k][1][s&&c==a[i+1]]+=f[i][j][k][l][s];
else
f[i+1][c][k][0][s&&c==a[i+1]]+=f[i][j][k][l][s];
}
}

ll ans=0;
for (i=0;i<=9;i++)
for (j=0;j<=a[0];j++)     //<=a[0]  !!!
for (k=0;k<=1;k++)
ans+=f[a[0]][i][j][0][k]*j;
ans++;
return ans;
}

int main()
{
while (scanf("%lld%lld",&n,&m)!=EOF&&n>=0)
{
n--;
cl(n,a);
cl(m,b);
if (a<0) printf("%lld\n",doit(b));
else printf("%lld\n",doit(b)-doit(a));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: