[NOIP2012 普及组] 摆花

传送门

题目描述

让你在花店前一共恰好摆m盆花,现在一共有n种花,每种花的最大数量为a[i],问你一共有多少种摆花方案。

思路一:暴力搜索

虽然暴搜铁T(过了3个测试点),先试着敲敲;dfs(x,k) x表示当前枚举到第几种花,k表示当前一共摆了k盆花。
从dfs(1,0)搜索,看有多少种方案数可使最后摆花的数量为k个,代码如下

int dfs(int x,int k) { //k代表个数,x代表当前枚举到第几种花 //记忆化搞定!
	if( k > m) {
		return 0;
	}
	if(k == m) {
		return 1;
	}
	if(x == n+1)
		return 0;
	int ans=0;
	for(int i=0; i<=a[x]; i++)
		ans =(ans + dfs(x+1,i+k))%mod;
	return ans;
}

想着加一下记忆化搜索上去,存储下,不让他重复搜,以为这样还会TLE,没想到AC了。但我还是觉得记忆化搜索写着题,不稳定,马上讲一下dp写法。

记忆化搜索代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#include<queue>
#include<vector>
#define endl '\n'
using namespace std;
void IOS() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
}
typedef pair<int,int> PII;
typedef long long ll;
const int mod = 1e6+7;
const int maxn=1e5+5;
int n,m;
int a[120];
int mp[120][120];
int dfs(int x,int k) { //k代表个数,x代表当前枚举到第几种花 //记忆化搞定!
	if( k > m) {
		return 0;
	}
	if(k == m) {
		return 1;
	}

	if(x == n+1)
		return 0;
	if(mp[x][k]) return mp[x][k];
	int ans=0;
	for(int i=0; i<=a[x]; i++)
		ans =(ans + dfs(x+1,i+k))%mod;
	mp[x][k] = ans;
	return ans;
}
int main() {
	cin >> n >> m;
	for(int i=1; i<=n; i++) cin>>a[i];
	cout<<dfs(1,0);
	return 0;
}

DP思路:凡是记忆化搜索能写的题,一定能用DP方程转移

定义DP[i][j] 表示前i个数的总和为j的方案数
显然

DP[i][j] = DP[i][j] + ∑ DP[i-1][j-k];

其中k表示当前枚举的是哪个数,这个数k显然 满足 [0,min(j,a[i]];

DP代码

int n,m;
cin >> n >> m;
for(int i=1; i<=n; i++)
	cin >> a[i];
dp[0][0] = 1;
for(int i=1; i<=n; i++) {
	for(int j=0; j<=m; j++) {
		for(int k=0; k<=min(j,a[i]); k++) {
			dp[i][j]=(dp[i][j]+dp[i-1][j-k])%mod;
		}
	}
}
cout<<dp[n][m];
posted @ 2021-10-29 16:47  Xiaomostream  阅读(165)  评论(0编辑  收藏  举报