hiho221 Push Button II

版权声明:转载我的原创博客情注明出处 https://blog.csdn.net/qq_31964727/article/details/82886990

目录

题目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;
}

猜你喜欢

转载自blog.csdn.net/qq_31964727/article/details/82886990
ii