(File IO): input:greatest.in output:greatest.out
时间限制: 1000 ms 空间限制: 131072 KB 具体限制
Goto ProblemSet
题目描述
已知若干个正整数的和为
,求这若干个正整数的最小公倍数的最大值。
输入
第一行一个整数
,表示测试数据的组数。
接下来
行,每行包括一个正整数
,表示若干个正整数的和为
。
输出
输出
行,每行包括一个整数,表示和为
的若干个正整数的最小公倍数的最大值。
样例输入
2
4
7
样例输出
4
12
数据范围限制
样例中第一组数据
,它能分解成
,很明显
时最小公倍数为
,是所有情况中最小公倍数最大的;第二组数据
,它能分解成
,
和
的最小公倍数是
,也是所有情况中最小公倍数最大的。
提示
%的数据:
;
%的数据:
,结果不会超过
类型;
%的数据:
,结果不会超过
位整数。
解题思路
心路历程:
开始看题
嗯……压根想不出任何方法。。。
再留意数据范围,我 (哔——),满分还要高精度?
(弃疗中………………)。。
最后膜拜巨老
。。。
分竟然过了。。
好吧,回归正题。。
首先我们明白,两个数的乘积等于两个数的最小公倍数乘最大公约数,
其次,我们还能想到,只要两个数所拥有的质因数不同,那么最大公约数也就很小,反之最大公约数就很大!!
(证明:x1+y1<x1*y1(2<=x1,y1),我们就可以分解质因数)
我们可以把题目变成一个背包问题
是背包的总容量,每个质因数和它的次幂都是一件物品,这些物品重量是它本身,价值是它本身乘原来的价值,
我们再根据上面所说的,两个数所拥有的质因数要不一样,所以,一个质数的次幂只能取一个。。
动态转移方程:
(下面附上我那很长的代码)
代码
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cmath>
using namespace std;
int t,n,zs[600],m,a[600][50],s,p,c[50];
bool flag;
void ch(int x,int y)
{
memset(c,0,sizeof(c));
int g=0;
for(int i=49; i>=1; i--)
{
c[i]=a[x][i]*y+g;
g=c[i]/10;
c[i]%=10;
}
}
bool hh(int x)
{
int i=1;
while(a[x][i]==0)i++;
i=49-i;
int j=1;
while(c[j]==0)j++;
j=49-j;
if(i>j)return 0;
if(j>i)return 1;
for(int ii=i; ii<=49; ii++)
{
if(a[x][ii]>c[ii])return 0;
if(c[ii]>a[x][ii])return 1;
}
}
int main()
{
freopen("greatest.in","r",stdin);
freopen("greatest.out","w",stdout);
for(int i=2; i<=501; i++)
{
flag=1;
for(int j=1; j<=t; j++)
{
if(i%zs[j]==0)
{
flag=0;
break;
}
}
if(flag==1)
{
zs[++t]=i;
}
}
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%d",&m);
memset(a,0,sizeof(a));
for(int i=0; i<=m; i++)
a[i][49]=1;
for(int j=1; j<=t; j++)
{
for(int i=m; i>=1; i--)
{
s=zs[j];
for(int k=1; s<=i; s*=zs[j])
{
ch(i-s,s);
if(hh(i))
{
for(int q=1; q<=49; q++)
a[i][q]=c[q];
}
}
}
}
p=1;
while(a[m][p]==0)p++;
while(p<=49)
{
cout<<a[m][p];
p++;
}
cout<<endl;
}
return 0;
}