BestCoder 1st Anniversary 1003(HDU5312)

4 篇文章 0 订阅

问题描述
BestCoder 1st Anniversary 1003(HDU5312)
Soda习得了一个数列, 数列的第n(n≥1)项是3n(n-1)+1. 现在他想知道对于一个给定的整数m, 是否可以表示成若干项上述数列的和. 如果可以, 那么需要的最小项数是多少? 输出最小花费

这道题很容易被当成贪心,然后就跪了。

后来看了题解也理解了很久,是三角形数的题,构造很巧妙。

这个题看上去是一个贪心, 但是这个贪心显然是错的. 事实上这道题目很简单, 先判断1个是否可以, 然后判断2个是否可以. 之后找到最小的k (k > 2)k(k>2), 使得(m - k) mod 6 = 0(m−k)mod6=0即可.

这里用到三角形数的一个性质:任意一个自然数最多只需要3个三角形数即可表示。

设f(x) = 3x(x-1)+1
M = f(x1) + f(x2) + f(x3) + …
假设最少可由k个f(x)表示,那么有: M = 6(k个三角形数的和) + k ,可以令k<6,那么k = M %6, 由于三角形数的性质,那么k>=3 的时,任意k个自然数可以被k个三角形数表示,假设成立,答案即为k.

所以要讨论一下k=0, k=1,k=2时的情况:
k=0 明显不成立,所以可以令 k=6 (6>=3), 显然成立且为最小值
先打一个f(x)<1e9的表,一共有大概19000个数,存在一个map里面,这样每次查询就可以在log级别查询
k=1时,直接查询map里是否有相应的键值,有的话结果为1,没有的话结果为7(7>=3)
同理k=2时 能找到一组满足条件的值,那么结果为2,否则结果为 8 (8 >= 3)

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<map>

using namespace std;

int form[20000];
int FN = 0;
int M;
map<int,int> Map;

void init()
{
    for(int i=1;;i++)
    {
        int temp = 3*i*(i-1) + 1;
        if(temp > 1e9) break;
        form[++FN] = temp;
        Map.insert(make_pair(temp,1));
    }
}

int main()
{
    init();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int M;
        scanf("%d",&M);
        int res = (M - 1)%6 + 1;
        if(res<3)
        {
            if(res == 1)
            {
                map<int,int>::iterator it;
                it = Map.find(M);
                if(it==Map.end()) printf("7\n");
                else printf("1\n");
            }
            else
            {
                bool flag = false;
                for(int i=1;form[i]<=M/2;i++)
                    if(Map.find(M-form[i])!=Map.end())
                    {
                        flag = true;
                        break;
                    }
                if(flag) printf("2\n");
                else printf("8\n");

            }
        }
        else
        {
            printf("%d\n",res);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值