1. 程式人生 > >【HZOI】 天明 (亂搞)

【HZOI】 天明 (亂搞)

天明

題目描述

話說,自從天明那次看到高月駕駛機關獸後,心裡十分癢癢,於是找到高月,要她教自己駕駛機關獸。 高月:“我可是學了一年吶!” 而且,說實話,高月覺得以天明的智商,機關術可能有點…… 於是乎,天明決定向月兒證明,自己的智商沒有問題!

天明四處打聽,終於打聽到了一種測智商的方法:將一個給定的數分成若干個互不 相等的數的和,使得分成的數的乘積最大,分成的數的乘積代入到一個正相關的函 數中所得的值就是自己的智商。 所以,你需要幫助天明,讓他可以為自己開出一張“高智商”證明。(就是作弊 啦……)

輸入

僅一行,一個整數 n,就是你要分解的那個數。(其中1 ≤ n ≤ 1000)

輸出

一個整數,就是 n 分解成若干個互不相等的數的最大乘積。

樣例輸入

7

樣例輸出

12

分析

通過這道題教大家一個道理:NOIP模擬=打表找規律=變成爆破兵。 教大家學會打表找規律的方法:

打個分析表

首先隨便寫一個dfs得到一個分析表。

何謂分析表? 就此題而言,如果只打一個表記錄n和對應的答案,即ans[7]=12; ans[8]=15;或者ans[1234]={0,1,2,3,4,6……};或者if(ans==7) return printf("12\n")*0;這樣的表是遠遠不夠我們找規律的!至少也要想這個樣子打表:

1->1 0
2->2 2
3->3 3
4->4 4
5->6 2*3
6->8 2*4
7->12 3*4
8->15 3*5
9->24 2*3*4
10->30 2*3*5
11->40 2*4*5
12->60 3*4*5
13->72 3*4*6
14->120 2*3*4*5
15->144 2*3*4*6
16->180 2*3*5*6
17->240 2*4*5*6
18->360 3*4*5*6
19->420 3*4*5*7
20->720 2*3*4*5*6
21->840 2*3*4*5*7
22->1008 2*3*4*6*7
23->1260 2*3*5*6*7
24->1680 2*4*5*6*7
25->2520 3*4*5*6*7
26->2880 3*4*5*6*8
27->5040 2*3*4*5*6*7
28->5760 2*3*4*5*6*8
29->6720 2*3*4*5*7*8
30->8064 2*3*4*6*7*8
31->10080 2*3*5*6*7*8
32->13440 2*4*5*6*7*8
33->20160 3*4*5*6*7*8
34->22680 3*4*5*6*7*9
35->40320 2*3*4*5*6*7*8
36->45360 2*3*4*5*6*7*9
37->51840 2*3*4*5*6*8*9
38->60480 2*3*4*5*7*8*9
39->72576 2*3*4*6*7*8*9
40->90720 2*3*5*6*7*8*9
41->120960 2*4*5*6*7*8*9
42->181440 3*4*5*6*7*8*9
43->201600 3*4*5*6*7*8*10
44->362880 2*3*4*5*6*7*8*9
45->403200 2*3*4*5*6*7*8*10
46->453600 2*3*4*5*6*7*9*10
47->518400 2*3*4*5*6*8*9*10
48->604800 2*3*4*5*7*8*9*10
49->725760 2*3*4*6*7*8*9*10
50->907200 2*3*5*6*7*8*9*10
51->1209600 2*4*5*6*7*8*9*10
52->1814400 3*4*5*6*7*8*9*10
53->1995840 3*4*5*6*7*8*9*11
54->3628800 2*3*4*5*6*7*8*9*10
55->3991680 2*3*4*5*6*7*8*9*11
56->4435200 2*3*4*5*6*7*8*10*11
57->4989600 2*3*4*5*6*7*9*10*11
58->5702400 2*3*4*5*6*8*9*10*11
59->6652800 2*3*4*5*7*8*9*10*11
60->7983360 2*3*4*6*7*8*9*10*11

不要問我為什麼在引用塊裡套程式碼塊

整體觀察與分段對比

整體觀察

這裡才開始講題

發現,不算上1,好像是把一個數拆得越散,乘積就會越大。

分段對比

比方說本題很明顯地看得出乘數個數的層次,即由幾個數乘起來。 那麼我們只要相信反正我是在找規律,這個層次推出來的規律一定適用於其它層次地集中火力搞某一個比較一般又比較好處理的層次

不信也沒關係,求出來一個規律再代入其它層次去看就是了。 如果分不出層次,就試試遞推吧;如果分出了層次,就把它當做分段遞推吧。

這裡才正式開始講題

就這個題而言,選一箇中間一點的層次:

27->5040 2*3*4*5*6*7
28->5760 2*3*4*5*6*8
29->6720 2*3*4*5*7*8
30->8064 2*3*4*6*7*8
31->10080 2*3*5*6*7*8
32->13440 2*4*5*6*7*8
33->20160 3*4*5*6*7*8
34->22680 3*4*5*6*7*9
35->40320 2*3*4*5*6*7*8

發現,27~34,都是6個數相乘,且正好27=2+3+4+5+6+7,27所對應的答案就是2*3*4*5*6*7=5040。而28的答案是2*3*4*5*6*8

2*3*4*5*6*7
2*3*4*5*6*8 (最後一個由7變到8)
2*3*4*5*7*8 (倒數第二個從6變到7)
2*3*4*6*7*8 (倒數第三個也加了1)
2*3*5*6*7*8 (倒數第四個也加了1)
2*4*5*6*7*8 (3+1=4)
3*4*5*6*7*8 (2+1=3)
3*4*5*6*7*9 (每個數都加了一以後,最後一位再加一個一)
2*3*4*5*6*7*8 (35=2+3+4+5+6+7+8,答案是2*3*4*5*6*7*8)

是不是有點感覺了?

再把這個猜想在其它層次裡看,發現正好合適,我應該想出了正確的規律耶。

↑是非常重要的,因為找規律的本質就是猜想猜想猜想猜想猜想猜想猜想猜想猜想猜想猜想猜想猜想吧。而猜想必須要經得起石劍實踐的檢驗才行。

最後成型的猜想就是這樣子的: (考試的時候寫的,比較很模糊不清,見諒)

打表發現把給定的數拆得更散答案更大。
當然,1是肯定不能拿出來的。

總之就是不管1,優先使項數最多。
只需要預處理一個2+3+4+5+6+......的數列出來然後找n在哪兩個數之間即可,這個甚至不需要lower_bound,直接列舉。

下面是根據打表觀察出的結論: 記上述數列為A,A[i]=2+3+......+i,則:
①若n=A[t],則ans=t!即2*3*4*......*t;
②若n∈[A[t]+1,A[t+1]-2],將t!後面的n-A[t]項都加上1,再乘起來。(這個情況可以包含①)
③若n=A[t+1]-1,將2到t的每個數都加上1後,最後一項再加上一,即ans=3*4*5*...*(t-1)*t*(t+2)

好像要打高精度!

亂寫一氣 我寫了些什麼。

程式碼

#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int MAXN=1000+20;
int x,cnt,weis,help[MAXN],tosum[MAXN];
long long arr[MAXN];

int main()
{
	for(int i=2;help[i-1]<=1200;i++) help[i]=help[i-1]+i;
	scanf("%d",&x);
	int i,k=2;
	while(x>=help[k]) k++; k--;
	for(i=2;i<=k;i++) tosum[++cnt]=i+(i>(k-x+help[k]));
	if(x==help[k+1]-1) tosum[cnt]++;
	
	weis=1; arr[1]=1;
	for(i=1;i<=cnt;i++)
	{
		for(int j=1;j<=weis;j++) arr[j]*=1LL*tosum[i];
		for(int j=1;j<weis;j++)
			arr[j+1]+=arr[j]/10,arr[j]%=10;
		if(arr[weis]>10) arr[weis+1]=arr[weis]/10,arr[weis]%=10,weis++;
	}
	for(int j=weis;j>=1;j--) printf("%lld",arr[j]);puts("");
}