Timus 1807. Cartridges for Maxim 解题报告(DP+数学)

96 篇文章 0 订阅

1807. Cartridges for Maxim

Time limit: 0.5 second
Memory limit: 64 MB
During a short break in a hot battle against Kappel's corps, Petka and Chapaev brought boxes with cartridges for the Anka's Maxim gun.
The Red Army men were exhausted because they had carried more than one box with cartridges to the machine gun, and each box contained at least one hundred cartridges. Anka noticed that there was the same number of cartridges in all boxes.
She wanted to put all the cartridges in several pockets so that the greatest common divisor of the numbers of cartridges in her pockets would be as large as possible. Among all the variants of such an arrangement, she wanted to choose the variant in which the least common multiple of the numbers of cartridges in her pockets would be as large as possible too. How could she do it?

Input

The only input line contains the total number  n of cartridges Petka and Chapaev brought (200 ≤  n ≤ 10 9).

Output

The only output line should contain integers  a 1a 2, …,  a k separated with a space (2 ≤  k ≤  n), where  a i is the number of cartridges Anka should put in the  ith pocket. If there are several answers, output any of them.

Samples

input output
200
100 100
625
375 250

    解题报告: 非常好的一道题,网上没找到解题报告,偶就详细写一下,大牛见笑~。

    首先,看清题意。给定一个整数n,200<n<10^9,且n是合数。将n分成几部分,使这几部分的最大公约数最大。到这里时,我们应该要想到,要使分成的几部分最大公约数最大,就要让这个最大公约数是n的最大的因数,m。且这几个数的最小公倍数最大,那么除去最大公约数,这几个数剩下的部分应当是互质的,且和为n/m。

    m是n最大的约数,那么n/m则是n最小的素因子。n<10^9,可以确定n/m<=(10^9)^0.5<32000。问题转化为将一个正整数k,k<32000,分为互质的几部分,让这几部分的积最大的问题。

    因为直接乘积会超出int,甚至long long的范围,我们可以使用对数,将乘法运算转化为加法运算,大大减少空间上的压力。

    经验上不会用到0-32000中所有的素数。可以自己测试,结果是到700的素数就够了。

    这个时候可以DP了。依次选择素数,判断1-maxn中能否更新出更大的值出来。能则保存。因为最后的结果需要打印路径,这里使用链表来记录中间过程。链表的大小也可以自己测试。

    质量很好的一道题,考察了很多细节,要非常注意写法,优化,不然很容易超时。在下代码如下:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
using namespace std;

int prime[701];
int primeNum;
bool h[701];
void calPrime()
{
    for(int i=2;i<=700;i++) if(!h[i])
    {
        prime[primeNum++]=i;
        for(int j=i+i;j<=700;j+=i)
            h[j]=true;
    }
}

const int maxn = 32000;
double v[maxn];

struct Node
{
    Node *pre;
    int v;
    int n;
    Node(){};
    Node(int _v, int _n, Node* _pre=0):
        v(_v), n(_n), pre(_pre)
    {
    }
} *has[maxn], node[2543000];
int cnt;

void init()
{
    calPrime();

    Node *tmp = new Node(1, 1);
    for(int i=1;i<maxn;i++)
	{
		cnt++;
        has[i]=node+cnt;
        node[cnt].v=1;
        node[cnt].n=1;
        node[cnt].pre=has[i-1];
	}

    for(int i=0;i<primeNum;i++)
    {
        int p=prime[i];
        double logtmp=log((double)p);

        for(int j=maxn-1;j>=p;j--)
        {
            int tmp=p;
            double sumtmp=logtmp;

            int lastpos=-1;
            int lastk;

            for(int k=1;tmp<=j;k++, tmp*=p, sumtmp+=logtmp) if(v[j-tmp]+sumtmp>v[j])
                v[j]=v[lastpos=j-tmp]+sumtmp, lastk=k;

            if(~lastpos)
            {
                cnt++;
                has[j]=node+cnt;
                node[cnt].v=p;
                node[cnt].n=lastk;
                node[cnt].pre=has[lastpos];
            }
        }
    }
}

int ans[20000];
int tol;

int main()
{
    init();

	int n;
    while(~scanf("%d", &n))
    {
        int m=n;
        for(long long i=2;i*i<=n;i++) if(n%i==0)
        {
            m=(int)i;
            break;
        }
		n/=m;

		if(m==2)
		{
			printf("%d %d\n", n, n);
		}
		else if(m==3)
		{
			printf("%d %d\n", n*2, n);
		}
		else
		{
			tol=0;
			Node *now=has[m];
			while(now)
			{
				ans[tol++]=n*(int)(pow((double)now->v, now->n)+0.5);
				now=now->pre;
			}

			sort(ans, ans+tol);
			printf("%d", ans[0]);
			for(int i=1;i<tol;i++) printf(" %d", ans[i]);
		    puts("");
		}
    }

    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值