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

题目描述:
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;
}
上一篇:简单数组与环形数组来实现队列


下一篇:【数据结构·考研】循环双端队列