51nod 1167 计算递推

博客介绍了如何利用递推式解决计算F(N)的问题,其中涉及到将N转化为K的幂的和,并转换为整数分解问题。通过动态规划的方法,将N分解为K的幂的和,从而求得方案数。文章给出了算法实现,包括转移方程的解释和K进制转换的过程,最后展示了具体的代码实现,时间复杂度为O(Klog3kN)。
摘要由CSDN通过智能技术生成

有这样一个递推式:

F(N) = 1 (0 <= N <= K - 1)

F(N) = F(N - K) + F(N / K) (N % K == 0)

F(N) = F(N - 1) (N % K != 0)

给出2个数K和N,计算F(N)。

 收起

输入

第1行:一个数K(2 <= K <= 10)。
第2行:大数N(1 <= N <= K^50)。

输出

输出F(N)

输入样例

5
12

输出样例

3

题目大意
有这样一个递推式:

F(N) = 1 (0 <= N <= K - 1) 
F(N) = F(N - K) + F(N / K) (N % K == 0) 
F(N) = F(N - 1) (N % K != 0)

给出2个数K和N,计算F(N)。

2≤K≤10 1≤N≤k50k50
分析
把F(N) = 1 (0 <= N <= K - 1)改成f(0)=1,然后答案一样。

考虑从0转移到N的一个方案,显然它是若干次加一和乘K。

转换一下思路:现在有一个数字集,开始为空。从0转移到N,加1的时候,相当于给集合添加一个1。乘K就相当于数字集里每个数乘K。最终N可以被表示为数字集里所有数的和。很显然每一个数都是K的幂。并且显然一种方案对应了唯一的转移方案。

那么就转换了问题:把N分解成K的幂的和,问有多少种方案。

然后就联想到51nod上的另一道题:整数分解为2的幂 
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1048

那么就可以DP了。设f[i][j]表示把KiKi分解为最大数是KjKj的方案数。不过和这道题的区别是,由f[i-1][]得到f[i][]时要合并k块。 
DP理解不了请看这里http://blog.csdn.net/worldwide_d/article/details/54091940

要合并K块,就得再套一个DP:设g[j][x]表示把j∗Kij∗Ki分成最大数是KxKx的方案数,然后g[j][x]=∑xx′=0g[j−1][x′]∗f[i−x′−1][x−x′]g[j][x]=∑x′=0xg[j−1][x′]∗f[i−x′−1][x−x′] 
最后的f[i][j]=g[K][j]f[i][j]=g[K][j] 
合并答案,就是把N转成K进制,然后剩下的和上面一样。

时间复杂度O(Klog3kN)
 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 52
#define M 155
#define mo 100000000
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note{
    int w,a[M];
    void operator = (const note b){
        int i;
        w=b.w;
        fo(i,1,w)a[i]=b.a[i];
    }
};note f[N][N],g[N][N],h[11][N],ans,n,c;
long long tt[260];
int i,j,k,K,l,ys,x,tot,wz;
int xx;
char s[32];
note operator + (const note &a,const note &b){
    c.w=a.w>b.w?a.w:b.w;
    memset(c.a,0,sizeof(c.a));
    int i;
    fo(i,1,c.w){
        c.a[i]+=i<=a.w?a.a[i]:0;
        c.a[i]+=i<=b.w?b.a[i]:0;
        c.a[i+1]+=c.a[i]/mo;
        c.a[i]%=mo;
    }
    if(c.a[c.w+1])c.w++;
    return c;
}
note operator * (const note &a,const note &b){
    int i,j;
    c.w=a.w+b.w-1;
    fo(i,0,c.w+1)tt[i]=0;
    long long kk;
    fo(i,1,a.w)fo(j,1,b.w){
        kk=1ll*a.a[i]*b.a[j];
        tt[i+j-1]+=kk;
    }
    fo(i,1,c.w){
        c.a[i]=tt[i]%mo;
        tt[i+1]+=tt[i]/mo;
        c.w=((tt[i]>=mo)&&(i==c.w))?c.w+1:c.w;
    }
    return c;
}
int main(){
    scanf("%d%s",&K,s+1);
    l=strlen(s+1);
    n.w=l/8+1;
    fo(i,1,l){
        int j=(l-i)/8;
        n.a[j+1]=n.a[j+1]*10+(s[i]-'0');
    }
    while(!n.a[n.w]&&n.w>1)n.w--;
    f[0][0].w=f[0][0].a[1]=1;
    fo(i,1,50){
        fo(j,0,i-1)g[1][j]=f[i-1][j];
        fo(j,2,K){
            fo(x,0,i-1){
                g[j][x].w=0;
                memset(g[j][x].a,0,sizeof(g[j][x].a));
                fo(k,0,x)g[j][x]=g[j][x]+g[j-1][k]*f[i-1-k][x-k];
            }
        }
        fo(j,0,i-1)f[i][j]=g[K][j];
        f[i][i].w=f[i][i].a[1]=1;
    }
    memset(g,0,sizeof(g));
    g[0][0].w=g[0][0].a[1]=1;
    wz=51;
    fo(i,0,50){
        ys=0;
        fd(j,n.w,1){
            x=n.a[j];
            n.a[j]=(1ll*ys*mo+x)/K;
            ys=(1ll*ys*mo+x)%K;
        }
        while(!n.a[n.w]&&n.w>1)n.w--;
        fo(j,0,i)h[0][j]=g[i][j];
        fo(j,1,ys)
            fo(x,0,i){
                h[j][x].w=0;
                memset(h[j][x].a,0,sizeof(h[j][x].a));
                fo(k,0,x)h[j][x]=h[j][x]+h[j-1][k]*f[i-k][x-k];
            }
        fo(j,0,i)g[i+1][j]=h[ys][j];
        if(n.w==1&&n.a[1]==0){
            n.w=0;
            wz=i+1;
            break;
        }
    }
    fo(i,0,50)ans=ans+g[wz][i];
    printf("%d",ans.a[ans.w]);
    fd(i,ans.w-1,1){
        xx=log(ans.a[i])/log(10)+1;
        fo(j,1,8-xx)putchar('0');
        printf("%d",ans.a[i]);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值