[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];