动态规划

P1077 摆花

2020-05-27  本文已影响0人  yuq329

P1077 摆花

题目描述

小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共m盆。通过调查顾客的喜好,小明列出了顾客最喜欢的n种花,从1n标号。为了在门口展出更多种花,规定第i种花不能超过a_i盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列。
试编程计算,一共有多少种不同的摆花方案。

输入格式

第一行包含两个正整数nm,中间用一个空格隔开。
第二行有n个整数,每两个整数之间用一个空格隔开,依次表示a_1,a_2,…,a_n

输出格式

一个整数,表示有多少种方案。注意:因为方案数可能很多,请输出方案数对1000007取模的结果。

输入输出样例

输入
2 4
3 2
输出
2

说明/提示

【数据范围】

对于20%数据,有0<n≤8,0<m≤8,0≤a_i≤8

对于50%数据,有0<n≤20,0<m≤20,0≤a_i≤20

对于100%数据,有0<n≤100,0<m≤100,0≤a_i≤100

NOIP 2012 普及组 第三题

解题思路

可以考虑动态规划,定义状态dp[i][j]表示前i种花占有前j个位置的方案数,那么可以看出dp[i][j]只与dp[i-1][j-k](前i-1种花占有前j-k个位置的方案数)有关,此处的k即为第i种花可能的摆放数量。
可得状态转移方程:

for (int j = 0; j <= m; j++)
    for (int k = 0; k <= min(a[i], j); ++k)
        dp[i][j] = (dp[i][j] + dp[i - 1][j - k]) % M;

可以得到c++代码

#include <iostream>

#define M 1000007
#define ll long long
using namespace std;
const int maxn = 101;
ll dp[maxn][maxn];
int a[maxn];

int main() {
    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(a[i], j); ++k)
                dp[i][j] = (dp[i][j] + dp[i - 1][j - k]) % M;

    cout << dp[n][m];
}

DP优化利用滚动数组,降低内存消耗

#include <iostream>

#define M 1000007
using namespace std;
const int maxn = 101;
int dp[2][maxn], a[maxn], t;

int main() {
    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) {
        t = 1 - t;
        for (int j = 0; j <= m; j++){
            dp[t][j] = 0;
            for (int k = 0; k <= min(a[i], j); ++k)
                dp[t][j] = (dp[t][j] + dp[1 - t][j - k]) % M;
        }
    }
    cout << dp[t][m];
}

DP优化 转变为一维动态规划,01背包问题
问题可以看作\sum_{i=1}^n{a_i}盆花按顺序排列,挑选出m盆的方案数
dp[i]表示长度为i的子序列有多少种.

#include <iostream>

#define M 1000007
using namespace std;
const int maxn = 101;
int dp[maxn], a[maxn];

int main() {
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) cin >> a[i];

    dp[0] = 1;
    for (int i = 1; i <= n; ++i)
        for (int j = m; j >=0; --j)//倒序,防止使用到已经处理过的dp[j - k]
            for (int k = 1; k <= min(a[i], j); ++k)
                //前i盆花可以取到j位置的方案数
                dp[j] = (dp[j] + dp[j - k]) % M;
    cout << dp[m];
}

搜索方法深度优先遍历

//来自:https://www.luogu.com.cn/blog/chenzhengyv/ti-xie-p1077-bai-hua-post
#include<bits/stdc++.h>
using namespace std;
const int maxn=105, mod = 1000007;
int n, m, a[maxn];
int dfs(int x,int k)
{
    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, k+i))%mod;
    return ans;
}
int main()
{
    cin>>n>>m;
    for(int i=1; i<=n; i++) cin>>a[i];
    cout<<dfs(1,0)<<endl;
    return 0;
}

记忆化搜索

//来自:https://www.luogu.com.cn/blog/chenzhengyv/ti-xie-p1077-bai-hua-post
#include<bits/stdc++.h>
using namespace std;
const int maxn=105, mod = 1000007;
int n, m, a[maxn], rmb[maxn][maxn];
int dfs(int x,int k)
{
    if(k > m) return 0;
    if(k == m) return 1;
    if(x == n+1) return 0;
    if(rmb[x][k]) return rmb[x][k]; //搜过了就返回
    int ans = 0;
    for(int i=0; i<=a[x]; i++) ans = (ans + dfs(x+1, k+i))%mod;
    rmb[x][k] = ans; //记录当前状态的结果
    return ans;
}
int main()
{
    cin>>n>>m;
    for(int i=1; i<=n; i++) cin>>a[i];
    cout<<dfs(1,0)<<endl;
    return 0;
}
上一篇下一篇

猜你喜欢

热点阅读