hdu 4427 Math Magic(DP)

题意:给出k个数的和n,它们的最小公倍数m,求有多少种数的排列满足要求。

思路:开始还以为是数学题,没想到是dp……第二次因为用了long long超时了,也可能是我写的效率比较低吧。。。用dp[i][j][k]表示前i个数的和是j,最小公倍数是k的种数,状态转移方程也不是很难想,枚举一下m的因子就好了。

 

代码:

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=1000+10;
const int mod=1000000000+7;
int dp[2][maxn][40];
int a[40],conv[maxn],b[40][40];
int n,m,k,cnt;
int gcd(int a,int b)
{
    int c;
    while(b!=0)
    {
        c=a%b;
        a=b;
        b=c;
    }
    return a;
}
int lcm(int a,int b)
{
    return a*b/gcd(a,b);
}
void Init()
{
    memset(dp,0,sizeof(dp));
    memset(conv,0xff,sizeof(conv));
    cnt=0;
    for(int i=1;i<=m;++i)
    {
        if(m%i==0)
        {
            a[cnt]=i;
            conv[i]=cnt++;
        }
    }
    int tmp;
    for(int i=0;i<cnt;++i)
      for(int j=0;j<cnt;++j)
      {
          tmp=lcm(a[i],a[j]);
          if(tmp<=m&&conv[tmp]!=-1)
          b[i][j]=conv[tmp];
      }
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(~scanf("%d%d%d",&n,&m,&k))
    {
        Init();
        for(int i=0;i<cnt;++i)
          dp[1][a[i]][i]=1;
        int wh,tmp;
        for(int i=2;i<=k;++i)
        {
            wh=i&1;
            for(int j=0;j<=n;++j)
              for(int z=0;z<cnt;++z)
                dp[wh][j][z]=0;
            for(int j=i;j<=n;++j)
            {
                for(int z=0;z<cnt;++z)
                {
                    if(j<a[z]) break;
                    for(int u=0;u<cnt;++u)
                    {
                        tmp=b[z][u];
                        dp[wh][j][tmp]+=dp[wh^1][j-a[z]][u]%mod;
                        dp[wh][j][tmp]%=mod;
                    }
                }
            }
        }
        printf("%d\n",dp[k&1][n][conv[m]]%mod);
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值