hiho221 Push Button II

目錄

題目1 : Push Button II

題意分析:

1.題是什麼?

2.思路

ac代碼:


題目1 : Push Button II

時間限制:10000ms

單點時限:1000ms

內存限制:256MB

描述

There are N buttons on the console. Each button needs to be pushed exactly once. Each time you may push several buttons simultaneously.

Assume there are 4 buttons. You can first push button 1 and button 3 at one time, then push button 2 and button 4 at one time. It can be represented as a string "13-24". Other pushing way may be "1-2-4-3", "23-14" or "1234". Note that "23-41" is the same as "23-14".

Given the number N your task is to find the number of different valid pushing ways.

輸入

An integer N. (1 <= N <= 1000)

輸出

Output the number of different pushing ways. The answer would be very large so you only need to output the answer modulo 1000000007.

樣例輸入

3

樣例輸出

13

題意分析:

1.題是什麼?

    將問題抽象化就是給你n個數字1~n,將所有數字以'-'分塊,要輸出的答案就是不重複的分塊方法總數(由於總數太大要求餘)

    而什麼是重複呢,分塊個數相同且每個對應的分塊中的數字組合是相同的則視爲重複,就好像現在要計算1234四個數字的分塊方法,其中兩種方法12-34與21-43,第一個分塊內都是1和2,第二個分塊內都是3和4,故而他們是重複的.

2.思路

    這道題是hiho220的問題變形,故而可以順手瞭解一下我關於 hiho220 的解題博客,那道題是要輸出所有的方法是怎樣的,n最大爲8而已故而使用的dfs填空,這道題其實題幹一模一樣,不過改爲要求輸出所有方法數目,並不需要關心每個方法具體是怎樣的組合,相應的n也變爲上限1000,故而原本的dfs填空必爆.需要改變解法.

    我們發現我們現在只需要輸出所有方法數目,並不關心每個方法具體組合,這種問題特徵幾乎是線性dp的標誌.而題中n爲1時答案確定爲1,故而更堅定了是線性dp.

    現在方向確定爲線性dp後判斷時間空間,1000的數據量級做n*n的dp時間空間都沒問題,然後思考最關鍵的dp遞推公式,線性dp的關鍵在於找到問題在n處的答案與前方在n-1處的答案的聯繫即遞推關係.

    針對與這個問題,我們列舉當n爲2時,答案爲3: 12    1-2    2-1這三種,我們也可以將之看作:

        ?-?12-?           (1)

        ?-?1-?-?2-?      (2)

        ?-?2-?-?1-     (3)

    ?表示爲空,我們會發現如果我們想在n爲2的答案的基礎上推出n爲3的答案,一定是基於n爲2的某個答案,將數字3加入其某個分塊或使數字3自成一個分塊,上面的?就是代表所有的這兩種情況.

    dp[i][j]中i與j的意義設計是線性dp最關鍵的一點,遞推數組最初我設計爲二維,dp[1000][1000],dp[i][j]我的設計中表示n爲i時分塊爲j個的答案種數.

     而就像dp[3][2],表示n爲3時有2個分塊的答案總數,它自然來自於n=2時的答案的延伸,也就是

      (1).n=2時對分塊爲1的答案將3自成一個分塊,1+1=2個分塊

      (2).n=2時對分塊爲2的答案將3加入其中某個分塊

    只有這兩種情況可以延伸出dp[3][2].故而遞推公式 dp[i][j]=j*dp[i-1][j-1]+j*dp[i-1][j];

for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) dp[i][j]=0;
for(int i=1;i<=n;i++){
        dp[i][1]=1;
        for(int j=2;j<=n;j++) dp[i][j]=(j*(dp[i-1][j-1]+dp[i-1][j]))%mod;
}

用時13ms,這裏發現n的答案種數僅僅與n-1有關,順手用了個滾動數組節約一下空間,不懂滾動數組可以來我的博客先了解一下 滾動數組  優化後:

int now=0;
for(int i=1;i<=n;i++){
	now=(now+1)%2;
	dp[now][1]=1;
	for(int j=2;j<=n;j++) dp[now][j]=(j*(dp[(now+1)%2][j-1]+dp[(now+1)%2][j]))%mod;
	dp[now][i+1]=0; //特殊的初始化方式
}

用時6ms,然後又發現好像滾動數組都用不着,因爲數據的遞推方式很特別,於是最終發現了最優解法建立於一維數組:

for(int i=1;i<=n;i++){
	dp[1]=1;
	for(int j=i;j>=2;j--) dp[j]=(j*(dp[j-1]+dp[j]))%mod;
	dp[i+1]=0; //特殊的初始化方式
}

用時3ms AC,再想了想.....應該沒什麼可優化的了,花15分鐘從13ms優化到了3ms,用900000ms的時間換來的10ms的優化,emmmm....不知道說什麼好.....

 

ac代碼:

#include <stdio.h>
typedef long long ll;
const int maxn=1005;
const ll mod=1000000007;
ll dp[maxn];
void solve(){
	int n;
	scanf("%d",&n);
	
	for(int i=1;i<=n;i++){
		dp[1]=1;
		for(int j=i;j>=2;j--) dp[j]=(j*(dp[j-1]+dp[j]))%mod;
		dp[i+1]=0; //特殊的初始化方式
	}
	ll ans=0;
	for(int i=1;i<=n;i++) ans=(ans+dp[i])%mod; 
	printf("%lld\n",ans);
}
//https://blog.csdn.net/qq_31964727/article/details/82886990 解題博客瞭解一下
int main(){
	solve();
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章