hdu 4427 Math Magic(DP)

来源:互联网 发布:学编程还是网页设计好 编辑:程序博客网 时间:2024/05/19 09:41

题意:给出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;}


 

原创粉丝点击