The Staircases(dp)(思维)

4 篇文章 0 订阅

One curious child has a set of N little bricks. From these bricks he builds different staircases. Staircase consists of steps of different sizes in a strictly descending order. It is not allowed for staircase to have steps equal sizes. Every staircase consists of at least two steps and each step contains at least one brick. Picture gives examples of staircase for N=11 and N=5:

Your task is to write a program that reads from input numbers N and writes to output numbers Q - amount of different staircases that can be built from exactly N bricks.

Input

Numbers N, one on each line. You can assume N is between 3 and 500, both inclusive. A number 0 indicates the end of input.

Output

Numbers Q, one on each line.

Sample Input

3
5
0

Sample Output

1
2

方法一:非常巧妙的方法(旋转?)(逆向?)
从下面一行一行得消除

#include<iostream>
#include<string.h>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<string>
using namespace std;
long long dp[520][520];

int main(){
	int n;
	for(int i=1;i<=500;i++) dp[i][1]=1;
    for(int i=1;i<=500;i++)
        for(int j=1;j<i;j++)
        	for(int k=1;k*j<=i;k++)
                dp[i][j]+=dp[i-k*j][j-1];
	while(~scanf("%d",&n)){
		if(n==0) break;
//		for(int i=1;i<=n;i++){
//			for(int j=1;j<=n;j++){
//				printf("dp[%d][%d]:%d\n",i,j,dp[i][j]);
//			}
//		}
	long long ans=0;
	for(int i=2;i<n;i++) ans+=dp[n][i];
	printf("%lld\n",ans);	
	}
	
}

方法二:
自己胡乱写的代码
思想是一列一列得减

#include<iostream>
#include<string.h>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<string>
using namespace std;
long long dp[520][520];

int main(){
	int n;
		for(int i=1;i<=500;i++) dp[i][i]=1;
		for(int i=3;i<=500;i++){
			for(int j=1;j<i;j++){
				for(int k=1;k<=i-j;k++){
					if(k<j){
					dp[i][j]+=dp[i-j][k];
//					printf("*i:%d j:%d  dp[%d][%d]:%d\n",i,j,i-j,k,dp[i-j][k]);						
					}

				}
			}
		}	
	while(~scanf("%d",&n)){
		if(n==0) break;

//		for(int i=1;i<=n;i++){
//			for(int j=1;j<=n;j++){
//				printf("dp[%d][%d]:%d\n",i,j,dp[i][j]);
//			}
//		}
	long long ans=0;
	for(int i=2;i<n;i++) ans+=dp[n][i];
	printf("%lld\n",ans);	
	}
	
}

这个是网上的代码hhhhhh(好简洁)

#include<iostream>
#include<string.h>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<string>
using namespace std;
long long dp[520][520];

int main(){
	int n;
    dp[0][0]=1;
    for(int i=1;i<=500;i++)
        for(int j=1;j<=i;j++)
            for(int k=0;k<j;k++)
                dp[i][j]+=dp[i-j][k];

	while(~scanf("%d",&n)){
		if(n==0) break;

//		for(int i=1;i<=n;i++){
//			for(int j=1;j<=n;j++){
//				printf("dp[%d][%d]:%d\n",i,j,dp[i][j]);
//			}
//		}
	long long ans=0;
	for(int i=2;i<n;i++) ans+=dp[n][i];
	printf("%lld\n",ans);	
	}
	
}

1:注意long long。因为转移方式很多,以及求的是方案数。及时n最大500,dp数组也会爆掉
提交前应该自己测一测的。
2:这题感觉挺神奇的,把所有状态转移一遍。不合法的设成0来使得转移无效。但是一开始合法状态是从不合法状态转移来的,把某些不合法状态设为1使得转移合法(笑)
3:这个状态选取感觉也挺神奇的
4:这个题的核心是想出状态转移的方法。就是所有合法的状态肯定是从只有一列的状态转移而来

一般的dp题都是把合法的状态赋值,合法推合法
但是这个题合法边界无限鸭嘤嘤嘤
合法边界枚举起来太麻烦了
n2好像可以枚举合法边界
普遍做法是把特殊的不合法边界赋值1,使得可以推出合法边界。不合法边界赋值0,推出不合法状态但不影响结果。然后所有的状态暴力推推推
即使这些推导是不合理的也没关系

完整版应该是设成三维
dp[i][j][k]
i:一共i个正方形
j:j列
k:最后一列高度为k

然后就一直sf。。。。。

#include<iostream>
#include<string.h>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<string>
using namespace std;
long long dp[520][520][520];
//不能AC的代码 
int main(){
	int n;
	memset(dp,0,sizeof(dp));
		for(int i=1;i<=500;i++){
			for(int j=i+1;i+j<=500;j++){
				dp[i+j][2][j]=1;
			}
		}
		for(int i=3;i<=500;i++){
			for(int j=3;j<=500;j++){
				for(int k=j;k<i;k++){
					for(int x=1;x<i&&x<k;x++){
						if(i-k>=2&&dp[i-k][j-1][x]!=0)
						dp[i][j][k]+=dp[i-k][j-1][x];
					}
				}
			}
		}	
	while(~scanf("%d",&n)){
		if(n==0) break;
	long long ans=0;
		for(int j=2;j<=n;j++)
			for(int k=1;k<n;k++){
				ans+=dp[n][j][k];
			}
	printf("%lld\n",ans);	
	}
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值