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;
}
posted @ 2021-07-24 16:51  yys_c  阅读(51)  评论(0编辑  收藏  举报