hdu 5312 Sequence
问题描述
Soda习得了一个数列, 数列的第n(n≥1)项是3n(n-1)+1. 现在他想知道对于一个给定的整数m, 是否可以表示成若干项上述数列的和. 如果可以, 那么需要的最小项数是多少?
例如, 22可以表示为7+7+7+1, 也可以表示为19+1+1+1
输入描述
输入有多组数据. 第一行有一个整数T(1≤T≤104 ), 表示测试数据组数. 然后对于每组数据:
一行包含1个整数 m(1≤m≤109).
输出描述
对于每组数据输出最小花费.
输入样例
10
1
2
3
4
5
6
7
8
22
10
输出样例
1
2
3
4
5
6
1
2
4
4
对于这种题,首先一开始就要对给出的公式进行研究,从这里入手是正道。
分析公式 3n(n−1)+1 ,若给出一个数n,假设要k个3n(n−1)+1加起来得到n,即 3n(n-1)k+k=n,注意到3n(n-1)k是6的倍数,那么求的是满足(n-k)%6==0的最小的k。还有就是要注意到1、2要特判,即k要从3开始枚举
打表发现规律了就好做了、 当n%6==1 时要考虑能否由1个数组成在表中找到就可以 否则就需要7项的和了(规律跑个程序或者列举一些数就发现了)
当n%6==2时 要考虑能否由2个数组成 根据表中数据列举一些数(跑个程序更快更简洁)会发现只有2和8两种情况
剩下的就注意一下是6的倍数的直接输出6否则输出n%6即可
/*附上渣渣程序
#include<stdio.h>
int main()
{
int i, j;
for(i = 7; i <= 100; i++)
{
for(j = 1; j < 10; j++)
{
if((i-j) % 6 == 0 && i != j && i%6==1)
printf("%d %d\n", i, j);
}
}
for(i = 7; i <= 100; i++)
{
for(j = 1; j < 10; j++)
{
if((i-j) % 6 == 0 && i != j && i%6==2)
printf("%d %d\n", i, j);
}
}
return 0;
}
*/
#include<cstdio> #include<cstring> #include<stack> #include<vector> #include<queue> #include<cmath> #include<cstdlib> #include<algorithm> using namespace std; const int oo = 0x3f3f3f3f; const int maxn = 1e6+7; const int mod = 1e9+7; int Soda[maxn]={1}, id;///id为最后一个数的下标 void init()///打表 { for(int i=1; i<=20000; i++) { Soda[i]=3*i*(i-1)+1; if(Soda[i] >= mod-7) {id = i; break;} } } int Search(int left, int right, int x)///二分查找 { int mid = (left+right)/2; if(Soda[mid] == x) return 1; if(left > right) return 0; if(Soda[mid] < x) return Search(mid+1, right, x); else return Search(left, mid-1, x); } int main() { init();///打表 int T, i, n, ans; // for(i = 0; i < 20; i++) // printf("%d\n", Soda[i]); scanf("%d", &T); while(T--) { scanf("%d", &n); if(n%6==1) { if(Search(0, id, n)) ans = 1; else ans = 7; } else if(n%6==2) { int ok = 0, p = id; for(i = 1; i <= p; i++) { while(Soda[i] + Soda[p] > n) p--; if(Soda[i]+Soda[p] == n) { ok = 1; break; } } if(ok) ans = 2; else ans = 8; } else { ans = n%6; if(ans == 0) ans = 6; // ans = (n-1)%6+1; } printf("%d\n", ans); } return 0; }