P1077 [NOIP2012 普及组] 摆花
一开始直接将题目按照字面意思翻译成dp,没想太多T了4个点...
\(f(i, j, k)\)表示摆好前i盆花,最后一种花是j,并且摆了k盆的所有摆法的集合,那么状态计算就是
\(f(i, j, k) = \sum_{u=1}^{j-1}\sum_{v=1}^{a_u}f(i-k,u,v)\),极其复杂,但是确实没错,需要2重循环更新
初始条件:\(f(i, j, k)=1,(i=k)\)
#include<iostream>
using namespace std;
const int mod = 1e6 + 7, N = 110;
int f[N][N][N];
int n, m;
int a[N];
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> a[i];
for(int i = 0; i <= m; i ++)
for(int j = 1; j <= n; j ++)
for(int k = 0; k <= a[j]; k ++)
if(i == k)
f[i][j][k] = 1;
for(int i = 2; i <= m; i ++){
for(int j = 1; j <= n; j ++)
for(int k = 1; k <= a[j] && k < i; k ++)
for(int u = 1; u < j; u ++)
for(int v = 1; v <= a[u]; v ++)
f[i][j][k] = (f[i][j][k] % mod + f[i - k][u][v] % mod) % mod;
}
int res = 0;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= a[i]; j ++)
res = (res % mod + f[m][i][j] % mod) % mod;
cout << res << endl;
return 0;
}
复杂度:\(O(N^5)\)
这一题给的必须升序摆放是迷惑信息
思路二:将问题转化为给定n个数字,每一个数字有一定的取值范围,求出令所有数字总和为n的所有取值方法,方法可以有暴力,记忆化,dp...
\(f(i, j)\)表示对于前i个数取值,和为j的所有取值方法的集合
\(f(i, j) = \sum_{k=0}^{a_i}f(i-1, j-k)\)
初始条件:\(f(0, 0) =1\)
复杂度:\(O(N^3)\)
#include<iostream>
using namespace std;
const int N = 110, mod = 1e6 + 7;
int f[N][N];
int a[N];
int n, m;
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> a[i];
f[0][0] = 1;
for(int i = 1; i <= n; i ++)
for(int j = 0; j <= m; j ++)
for(int k = 0; k <= a[i] && k <= j; k ++)
f[i][j] = (f[i][j] % mod + f[i - 1][j - k] % mod) % mod;
cout << f[n][m] << endl;
return 0;
}
等价变换代码
#include<iostream>
using namespace std;
const int N = 110, mod = 1e6 + 7;
int f[N];
int a[N];
int n, m;
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> a[i];
f[0] = 1;
for(int i = 1; i <= n; i ++)
for(int j = m; j >= 0; j --)
/*
注意此处k要从1开始,因为在二维dp中k从0开始的
目的是将f[i][j]赋值为f[i - 1][j],而一维情
况下f[j]已经是f[i - 1][j]了,所以k要从1开始
*/
for(int k = 1; k <= a[i] && k <= j; k ++)
f[j] = (f[j] % mod + f[j - k] % mod) % mod;
cout << f[m] << endl;
return 0;
}