• 动态规划-美丽序列


    链接:https://ac.nowcoder.com/acm/problem/21313
    来源:牛客网

    题目描述

    牛牛喜欢整数序列,他认为一个序列美丽的定义是
    1:每个数都在0到40之间
    2:每个数都小于等于之前的数的平均值
    具体地说:for each i, 1 <= i < N,  A[i] <= (A[0] + A[1] + ... + A[i-1]) / i.
    3:没有三个连续的递减的数

    现在给你一个序列,每个元素是-1到40,你可以将序列中的-1修改成任意的数,求你可以得到多少个美丽序列,答案对1e9+7取模

    输入描述:

    第一行输入一个整数n (1 ≤ n ≤ 40)

    第二行输入n个整数

    输出描述:

    输出一个整数
    示例1

    输入

    复制
    2
    3 -1

    输出

    复制
    4
    示例2

    输入

    复制
    3
    5 3 -1

    输出

    复制
    2
    示例3

    输入

    复制
    3
    -1 0 40

    输出

    复制
    0
    示例4

    输入

    复制
    11
    -1 40 -1 -1 -1 10 -1 -1 -1 21 -1

    输出

    复制
    579347890

    思路:类似牛牛和数组那一题。此题添加了另外两个限制。因此开了四维数组。
    #include <cstdio>
    #include <cstdlib>
    #include <climits>
    #include <fstream>
    #include <algorithm>
    #include <cmath>
    #include <deque>
    #include <vector>
    #include <queue>
    #include <string>
    #include <cstring>
    #include <map>
    #include <stack>
    #include <set>
    #include <sstream>
    #include <iostream>
    using namespace std;
    
    typedef long long ll;
    const int mod = 1e9+7;
    const double PI=3.14159265358;
    const int INF=0x3f3f3f3f;
    
    int n;
    //储存输入的数组
    ll a[42];
    //dp开四维数组:dp[i][j][k][h];
    //分别对应第i层,值为j,是递减序列中的第k个,且i层数组的前i个数的和是h;
    //把整数序列当成一个数组。所以i表示数组的长度
    //因为数组中每个数的范围是0~40,所以j表示0~40.
    //因为要求没有三个连续的递减的数。所以用k表示这个数在数组中是三个递减数的第一个。还是第二个
    //因为每个说都小于之前的数的平均数。所以用h表示在数组中从第一个直到第i个数的总和。
    ll dp[41][41][3][1610];
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
    
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
        }
    
    
        memset(dp,0,sizeof(dp));
        //判断第一个数,由于是第一个数,所以他只能是递减序列的第一个
        if(a[1]==-1){
            //由于是-1所以,第一个数可以是0~40中的任一一个数
            for(int i=0;i<=40;i++){
                //第1层值为i且是递减序列中第一个的且总和为i的可能性为1;
                dp[1][i][1][i]=1;
            }
        }else{
            //第1层值为a[i]且是递减序列中第一个的且总和为a[i]的可能性为1;
            dp[1][a[1]][1][a[1]]=1;
        }
    
        //从第二层往后推
        for(int i=2;i<=n;i++)
        {
            if(a[i]==-1){
                //第i层的数可以是0~40.用j表示
                for(int j=0;j<=40;j++){
                    //循环第i-1层的值是0~40.用l表示
                    for(int l=0;l<=40;l++){
                        //k表示到目前这一层的总和的值。
                        //由于小于等于前i-1层的数的总和的平均数。
                        //所以用j乘以i-1层表示k的最小值。
                        //最大值是40*40减去当前这一层的值。
                        for(int k=j*(i-1);k<=1600-j;k++)
                        {
                            //如果j>=l:表示这一层的值可以作为递减序列的第一个
                            if(j>=l){
                                //用第i层值为j的。且位于递减序列的第一个的。且总和为k+j的dp
                                //加上上一层的值为0~40.且位于递减序列的第一个和第二个的。且总和为k
                                //的dp相加。
                                dp[i][j][1][k+j]=(dp[i][j][1][k+j]+dp[i-1][l][1][k])%mod;
                                dp[i][j][1][k+j]=(dp[i][j][1][k+j]+dp[i-1][l][2][k])%mod;
                            }else{
                                //如果j<l。表示这个数只能是递减序列的第二个。
                                //第i层值为j的。且位于递减序列的第二个的。且总和为k+j的dp
                                //加上上一层的值为0~40.且位于递减序列的第一个。且总和为k的dp。
                                dp[i][j][2][k+j]=(dp[i][j][2][k+j]+dp[i-1][l][1][k])%mod;
                            }
                        }
                    }
                }
            }else{
                //类似上面的情况。不过是把j确定为a[i]了
                for(int l=0;l<=40;l++){
                    for(int k=a[i]*(i-1);k<=1600-a[i];k++){
                        if(a[i]>=l){
                            dp[i][a[i]][1][k+a[i]]=(dp[i][a[i]][1][k+a[i]]+dp[i-1][l][1][k])%mod;
                            dp[i][a[i]][1][k+a[i]]=(dp[i][a[i]][1][k+a[i]]+dp[i-1][l][2][k])%mod;
                        }else{
                            dp[i][a[i]][2][k+a[i]]=(dp[i][a[i]][2][k+a[i]]+dp[i-1][l][1][k])%mod;
                        }
                    }
                }
            }
        }
    
        ll ans=0;
        //循环当前n层的值为0~40的所有可能情况。
        for(int i=0;i<=40;i++){
            //循环所有可能和的情况。将k设置=i*n。是为了简便计算
            for(int k=i*n;k<=1600;k++){
                //需要累加位于递减序列的第一个和第二个的两种情况的dp的和。
                ans=(ans+dp[n][i][1][k])%mod;
                ans=(ans+dp[n][i][2][k])%mod;
            }
        }
        cout<<ans<<endl;
    
        return 0;
    }
  • 相关阅读:
    BZOJ 1101 [POI2007]Zap
    BZOJ 2005 [Noi2010]能量采集
    BZOJ 1053 [HAOI2007]反素数ant
    BZOJ 4321 queue2
    ZOJ 1456 Minimum Transport Cost(Floyd算法求解最短路径并输出最小字典序路径)
    POJ 3268 Silver Cow Party(Dijkstra算法求解来回最短路问题)
    Trie(字典树)解析及其在编程竞赛中的典型应用举例
    POJ 3037 Skiing(如何使用SPFA求解二维最短路问题)
    POJ 1724 ROADS(使用邻接表和优先队列的BFS求解最短路问题)
    POJ 1860 Currency Exchange(如何Bellman-Ford算法判断图中是否存在正环)
  • 原文地址:https://www.cnblogs.com/mzchuan/p/13589831.html
Copyright © 2020-2023  润新知