dp hdu-4427-Math Magic

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4427

题目大意:

给k个正整数的和n,最小公倍数m,求一共有多少种方案。

解题思路:

很明显的dp.

状态很好想,dp[][i][j]表示当前和为i,最小公倍数为j的方案数。

对于k个数中的任何一个数,它一定是m的约数,否则最小公倍数一定不为m(一个很大的剪枝).

用滚动数组保存。对于每个数,枚举可能取的值(m的约数),最多只有32个(1000内数的因数个数最多只有32个)。

然后滚动数组初始化时注意只用初始化可能到达的位置,其他的不用初始化1000*32就够了,我弱逼的搞成1000*1000,然后一直TLE。。。。

代码:

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<map>
#include<vector>
#include<set>
#include<queue>
#include<string>
using namespace std;

const int INF = 0x3f3f3f3f;
const double eps = 1e-6;
const double PI = acos(-1.0);
//typedef __int64 ll;
//#pragma comment(linker, "/STACK:1024000000,1024000000")
/*
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
*/
#define Maxn 1100
#define M 1000000007
int dp[2][Maxn][Maxn],lcm[1005][1005];
int n,m,k;
int yin[1100],cnt;

int gcd(int a,int b)
{
    if(a%b==0)
        return b;
    return gcd(b,a%b);
}
void pre(int x)
{
    for(int i=1;i<=x;i++)
        if(x%i==0)
            yin[++cnt]=i; //初始化时,不该对称的累加上去,那样剪枝的话就不是递增的了
    return ;
}

int main()
{
    //printf("%lld\n",(1LL<<31) -1);
    int i,j;

    for(i=1;i<=1000;i++)
        for(j=1;j<=1000;j++)
            lcm[i][j]=i/gcd(i,j)*j;

    while(~scanf("%d%d%d",&n,&m,&k))
    {
        cnt=0;
        pre(m);
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=cnt;i++)
            dp[0][yin[i]][yin[i]]=1;
        int cur=0,nex;

        for(int i=2;i<=k;i++)
        {
            nex=cur^1;
            for(int w=1;w<=n;w++) //只用初始化1000*32,弱逼的1000*1000果断超时了
                for(int q=1;q<=cnt;q++)
                    dp[nex][w][yin[q]]=0;
            for(int jj=1;jj<=cnt&&yin[yy]<=(n-(k-1));jj++)
               for(int ss=1;ss<=cnt;ss++)
                  {
                    int j=yin[jj];
                    int s=yin[ss];
                    if(m%lcm[j][s]) //不是的话继续
                        continue;
                     for(int p=i-1;p<=n-(k-i)-j;p++) //前面至少为i-1,正整数
                     if(dp[cur][p][s])
                         dp[nex][j+p][lcm[s][j]]=(dp[nex][j+p][lcm[s][j]]+dp[cur][p][s])%M;
                  }
            cur=nex;
        }
        printf("%d\n",dp[cur][n][m]);
    }
    return 0;
}





你可能感兴趣的:(动态规划)