ZOJ3662Math Magic(分组背包+完全背包)

I - Math Magic

Time Limit:3000MS     Memory Limit:32768KB     64bit IO Format:%lld
& %llu

Submit Status

Description

Yesterday, my teacher taught us about math: +, -, *, /, GCD, LCM... As you know, LCM (Least common multiple) of two positive numbers can be solved easily because of a * b = GCD (a, b) * LCM (a, b).

In class, I raised a new idea: "how to calculate the LCM of K numbers". It‘s also an easy problem indeed, which only cost me 1 minute to solve it. I raised my hand and told teacher about my outstanding algorithm. Teacher just smiled and smiled...

After class, my teacher gave me a new problem and he wanted me solve it in 1 minute, too. If we know three parameters N, M, K, and two equations:

1. SUM (A1, A2, ..., Ai, Ai+1,..., AK) = N

2. LCM (A1, A2, ..., Ai, Ai+1,..., AK) = M

Can you calculate how many kinds of solutions are there for Ai (Ai are all positive numbers). I began to roll cold sweat but teacher just smiled and smiled.

Can you solve this problem in 1 minute?

Input

There are multiple test cases.

Each test case contains three integers N, M, K. (1 ≤ N, M ≤ 1,000, 1 ≤ K ≤ 100)

Output

For each test case, output an integer indicating the number of solution modulo 1,000,000,007(1e9 + 7).

You can get more details in the sample and hint below.

Sample Input

4 2 2
3 2 2

Sample Output

1
2

Hint

The first test case: the only solution is (2, 2).

The second test case: the solution are (1, 2) and (2, 1).

题意:有K个正整数,和为N,最小公倍数为M,求有多少种组合。

分析:因这K个数是M的因子,而M的所有因子又是所有可能组成的最小公倍数的情况,然而N,M<=1000,所以1000以内的数的最多因子个数为35个,所以只需记录下来,用离散化的思想就可以解决。

#include<stdio.h>
#include<string.h>

const int MOD = 1e9+7;
int dp[2][1005][1005];
int LCM[1005][1005],N,M,K,num[105],cet;

int gcd(int a,int b)
{
    if(b==0) return a;
    return gcd(b,a%b);
}
int lcm(int a,int b)
{
    return (a*b)/gcd(a,b);
}

int main()
{
    for(int i=1;i<=1000;i++)
        for(int j=1;j<=1000;j++)
        LCM[i][j]=lcm(i,j);

    while(scanf("%d%d%d",&N,&M,&K)!=EOF)
    {
        cet=0;
        for(int i=1;i<=M;i++)
            if(M%i==0)
                num[++cet]=i;

        for(int i=0;i<=N;i++)//和
            for(int j=1;j<=cet;j++)//最小公倍数
            dp[0][i][num[j]]=0;
        dp[0][0][1]=1;

        int flag=0,ss,ff;
        for(int k=1;k<=K;k++)
        {
            flag^=1;
            for(int i=k;i<=N;i++)
            for(int j=1;j<=cet;j++)
            dp[flag][i][num[j]]=0;

            for(int s=k-1;s<=N;s++)//k-1个数至少和为k-1
            for(int c=1;c<=cet;c++)//每个最小公倍数
            {
                if(dp[flag^1][s][num[c]]==0)
                    continue;

                for(int i=1;i<=cet;i++)//每个因子
                {
                    ss=s+num[i];
                    ff=LCM[num[c]][num[i]];

                    if(ss>N||M%ff!=0)continue;

                    dp[flag][ss][ff]+=dp[flag^1][s][num[c]];
                    dp[flag][ss][ff]%=MOD;
                }
            }
        }

        printf("%d\n",dp[flag][N][M]);
    }
}
时间: 2024-11-03 22:10:40

ZOJ3662Math Magic(分组背包+完全背包)的相关文章

ZOJ 3164 Cookie Choice 分组背包 混合背包

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3181 题意: 就是混合背包加分组背包,有的物品是01背包,有的是多重背包,有的是完全背包,同时物品还有不超过8组的分组,如果在同一组则最多只能选一种.问能不能恰好地用掉D的容量,并且使所获价值最大. 分析: 开始的想法是多开一个下标,先把没有分组的做了,在0的下标,然后分组分别在组号的下标里按顺序处理,是什么背包就用什么做法,不过一直WA,对拍小数据大数据都没啥问题(可能随机

HDU1864_最大报销额(背包/01背包)

解题报告 题目传送门 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define inf 99999999 using namespace std; int v,w[35],d[4],dw1,sum,dp[31*1000*100]; int main() { double Q,dw; int n,i,j,m,t; char c; while(~sc

HDU1114_Piggy-Bank(背包/完全背包)

解题报告 题目传送门 题意: 给金币的面额和重量,求装满储蓄罐的最小价值. 思路: 完全背包基础,初始dp为最大,dp[0]=0表示储蓄罐为空价值为0; 状态转移方程就是dp[j]=min(dp[j],dp[j-w[i]]+c[i]) #include <iostream> #include <cstring> #include <cstdio> #define inf 99999999 using namespace std; int main() { int t,i

HDU2602_Bone Collector(背包/01背包)

解题报告 题目传送门 题意: 容量为v的大小,物品数n,每个物品有价值和容量,求能装进包的最大价值. 思路: 基础01背包. dp[j]=max(dp[j],dp[j-c[i]]+w[i]) #include <iostream> #include <cstring> #include <cstdio> #define inf 99999999 using namespace std; int main() { int t,i,j,n,v,w[1010],c[1010]

POJ 3260 多重背包+完全背包

前几天刚回到家却发现家里没网线 && 路由器都被带走了,无奈之下只好铤而走险尝试蹭隔壁家的WiFi,不试不知道,一试吓一跳,用个手机软件简简单单就连上了,然后在浏览器输入192.168.1.1就能看到他的路由器的一切信息,包括密码,然后打开笔记本……好了,废话不多说,能连上网后第一时间当然是继续和队友之前约好的训练了. 今天翻看到之前落下的一道混合背包题目,然后在草稿本上慢慢地写递推方程,把一些细节细心地写好…(本来不用太费时间的,可是在家嘛,一会儿妈走来要我教她玩手机,一会儿有一个亲戚朋

HDU1248_寒冰王座(背包/完全背包)

解题报告 题目传送门 无聊的水题. #include <iostream> #include <cstring> #include <cstdio> #define inf 0x3f3f3f3f using namespace std; int c[10100],w[10100],dp[11000],v,n; int main() { int t,i,j; scanf("%d",&t); while(t--) { memset(dp,0,si

HDU2191_悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(背包/多重背包)

解题报告 题目传送门 题意: 中文不多说; 思路: 基础多重背包,每个物品有多个可以选,转换成01背包解. #include <iostream> #include <cstring> #include <cstdio> #define inf 99999999 using namespace std; int main() { int t,i,j,n,m,v,p,h,cc,w[1010],c[1010],dp[1010]; scanf("%d",&

B 二维背包+完全背包 Hdu2159

<span style="color:#3333ff;">/* ------------------------------------------------------------------------------------------------ author : Grant Yuan time : 2014.7.19 aldorithm: 二维背包+完全背包 ----------------------------------------------------

B 维背包+完全背包 Hdu2159

<span style="color:#3333ff;">/* ------------------------------------------------------------------------------------------------ author : Grant Yuan time : 2014.7.19 aldorithm: 二维背包+全然背包 ----------------------------------------------------

POJ2063_Investment(背包/完全背包)

解题报告 题意: 本金买股票,最大n年后的收益(本金加利息) 思路: 基础完全背包,单纯的写可能TLE,因为股票都是1000的倍数,所以本金用1000的整数倍来买股票. #include <iostream> #include <cstring> #include <cstdio> #define inf 99999999 using namespace std; int w[12],c[12],dp[2001000],v; int n,d; int main() {