ZOJ 3662 dp+映射表

本文介绍了ACM竞赛中解决特定类型问题的动态规划方法,重点探讨了如何利用约数运算优化算法效率。通过实例解析,展示了如何预处理LCM表并使用动态规划实现高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4885

题解: 对于M, M的约数集合A满足任取x, y ∈A, 有LCM(x,  y)∈A, 于是,对于A中编号为i的元素x,设map[x] = i, 并有num[i] = x

于是,f[i][j][k]表示前i个数,和为j,LCM为num[i],递推式为 f[i + 1][j + x][map[LCM(x, k)]] += f[i][j][k]

然后预处理LCM(num[i], num[j]),记下标号

复杂度O(n * k * m) m为M以内的约数个数,实测m <= 50

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MOD 1000000007

using namespace std;


int lcm[50][50];
int f[3][1010][50];

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 cnt = 0;
int num[1000];
int map[1010];
int n, m, K;

int main()
{
    //freopen("test.in", "r", stdin);
	while(~scanf("%d%d%d", &n, &m, &K))
	{
		cnt = 0;
		for(int i = 1; i <= m; i++)
		{
			if(m % i == 0) num[cnt++] = i;
		}
		//for(int i = 0; i < cnt; i++) cout << num[i] << " ";
		for(int i = 0; i <= 1000; i++) map[i] = -1;
		for(int i = 0; i < cnt; i++) map[num[i]] = i;
		for(int i = 0; i < cnt; i++)
			for(int j = 0; j < cnt; j++)
				lcm[i][j] = map[lCM(num[i], num[j])];
				
		memset(f, 0, sizeof f);
		f[0][0][0] = 1;
		for(int i = 0; i < K; i++)
        {
            int ii = i & 1, jj = (i + 1) & 1;
            memset(f[jj], 0, sizeof(f[jj]));
            for(int j = 0; j < n; j++)
				for(int k = 0; k < cnt; k++)
				if(f[ii][j][k])
				{
					for(int p = 0; p < cnt; p++)
					{	
						int tmp = lcm[k][p];	
						if(tmp == -1) break;
						if(j + num[p] > n) break;													
						f[jj][j + num[p]][tmp] += f[ii][j][k];
						if (f[jj][j + num[p]][tmp] >= MOD) f[jj][j + num[p]][tmp] -= MOD;
						/*
                        if(lcm[num[k]][num[p]] > m) break;
						if(j + num[p] > n) break;
						f[jj][j + num[p]][lcm[num[k]][num[p]]] += f[ii][j][num[k]];
						if (f[jj][j + num[p]][lcm[num[k]][num[p]]] >= MOD) f[jj][j + num[p]][lcm[num[k]][num[p]]] -= MOD;						
						*/
					}
				}
        }	
		printf("%d\n", f[K & 1][n][map[m]]);		
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值