3092最小公倍数-完全背包问题

本题是一道关于最小公倍数(LCM)的数学问题,要求找到将给定数字S拆分成若干部分后的最大LCM,并对结果取模M。问题通过完全背包的策略解决,注意要考虑质数的次方以及避免计算过程中数值溢出。AC代码提供了解决此问题的正确思路。
摘要由CSDN通过智能技术生成

题目描述:
Least common multiple
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1869 Accepted Submission(s): 705

Problem Description
Partychen like to do mathematical problems. One day, when he was doing on a least common multiple(LCM) problem, he suddenly thought of a very interesting question: if given a number of S, and we divided S into some numbers , then what is the largest LCM of these numbers? partychen thought this problems for a long time but with no result, so he turned to you for help!
Since the answer can very big,you should give the answer modulo M.

Input
There are many groups of test case.On each test case only two integers S( 0 < S <= 3000) and M( 2<=M<=10000) as mentioned above.

Output
Output the largest LCM modulo M of given S.

Sample Input
6 23

Sample Output
6

Hint: you can divied 6 as 1+2+3 and the LCM(1,2,3)=6 is the largest so we output 6%23=6.

思路:由于拆分的数最终求最小公倍数,由最小公倍数特性,首先考虑只取0-s内的质数,用01背包求解,但是不得行。。。看了别人题解才知道,可能取到质数的次方,比如7,不应该是25而应该是34,因为四是二的次方,而二与三互质,所以三与四是互质的,所以四也应该放进物品里面去,由此转换为完全背包。
还需要考虑的是如果直接用整数的dp取乘积最大值会越界,要把乘积最大改为log求和最大,然后单独取一个ans数组保存最终解,每次修改dp数组时修改ans数组。
01背包错误代码:

#include<iostream>
#include<stdio.h>
#include<vector>
#include<string.h>
#include<string>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cmath>
#include<unordered_map>
#include<unordered_set>
using namespace std;
const int maxsize = 1e5 + 100;
double dp[maxsize];
int prim[maxsize];
int ans[maxsize];
int n = 0;
bool vis[maxsize] = { 0 };
int s, m;
void get() {
	for (int i = 2; i <= 3000; i++)
	{
		if (!vis[i])prim[n++] = i;
		else continue;
		for (int j = i; j <= 3000; j+=i) {
			vis[j] = 1;
		}
	}
}
int getres() {
    for (int i = 0; i <= s; i++){dp[i] = 0;ans[i]=1;}
    for (int i = 0; prim[i] <= s&&i<n; i++) {
        for (int j = s; j >= prim[i]; j--) {
            double t=log(prim[i]);
            if(dp[j - prim[i]]+t>dp[j]){
            dp[j]=dp[j-prim[i]]+t;
            ans[j]=ans[j-prim[i]]*prim[i]%m;
            }
        }
    }
    return ans[s];
}
int main() {
    get();
    while (cin >> s >> m) {
        cout << getres()<< endl;
    }
    return 0;
}

AC代码:

#include<iostream>
#include<stdio.h>
#include<vector>
#include<string.h>
#include<string>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cmath>
#include<unordered_map>
#include<unordered_set>
using namespace std;
const int maxsize = 1e5 + 100;
double dp[maxsize];
int prim[maxsize];
int ans[maxsize];
int n = 0;
bool vis[maxsize] = { 0 };
int s, m;
void get() {
	for (int i = 2; i <= 3000; i++)
	{
		if (!vis[i])prim[n++] = i;
		else continue;
		for (int j = i; j <= 3000; j+=i) {
			vis[j] = 1;
		}
	}
}//先求出3000内的质数,然后用完全背包求解。
int getres() {
	for (int i = 0; i <= s; i++) { ans[i] = 1; dp[i] = 0; }
	for (int i = 0; prim[i] <= s&&i<n; i++) {
		for (int j = s; j >= prim[i]; j--) {
			double t = log(double(prim[i]));
			for (int k = 1, p = prim[i];p <= j; p*=prim[i],k++) {
				if (dp[j] < dp[j - p] + k * t) {
					dp[j] = dp[j - p] + k * t;
					ans[j] = ans[j - p] * p % m;
				}
			}
		}
	}
	return ans[s]%m;
}
int main() {
	get();
	while (cin >> s >> m) {
		cout << getres()<< endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值