试题 算法提高 天天向上(dfs、dp)

问题描述
  A同学的学习成绩十分不稳定,于是老师对他说:“只要你连续4天成绩有进步,那我就奖励给你一朵小红花。”可是这对于A同学太困难了。于是,老师对他放宽了要求:“只要你有4天成绩是递增的,我就奖励你一朵小红花。”即只要对于第i、j、k、l四天,满足i<j<k<l并且对于成绩wi<wj<wk<wl,那么就可以得到一朵小红花的奖励。现让你求出,A同学可以得到多少朵小红花。
输入格式
  第一行一个整数n,表示总共有n天。第二行n个数,表示每天的成绩wi。
输出格式
  一个数,表示总共可以得到多少朵小红花。
样例输入
6
1 3 2 3 4 5
样例输出
6
数据规模和约定
  对于40%的数据,n<=50;
  对于100%的数据,n<=2000,0<=wi<=1e9。
思路
1.记忆化搜索
维护一个二维数组dp,pos代表当前位置,cnt代表是作为递增的第cnt天。dp[pos][cnt]存的是当前位置作为递增的第cnt天到cnt=4所能得到的方案数。
累加除倒数3个数的其余数作为递增序列的第一天的方案数即是答案。
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
ll a[2005];
ll dp[2005][5];
ll n,p,t;
ll dfs(int pos,int cnt){
    if(cnt==4)return 1;///达到4个递增数字,返回1
    if(dp[pos][cnt]!=-1)return dp[pos][cnt];///记忆化
    ll c=0;///当前位置的递增天数到能满足递增4天的方案数,注意是局部变量
    for(int i=pos+1;i<n;i++){
        if(a[i]>a[pos]){
            c+=dfs(i,cnt+1);
        }
    }
     return dp[pos][cnt]=c;///记忆一波返回
}
int main(){
    cin>>n;
    memset(dp,-1,sizeof(dp));
    for(int i=0;i<n;i++)cin>>a[i];
    ll ans=0;
    for(int i=0;i<n-3;i++){
        ans+=dfs(i,1);///累加每个数作为递增序列的第一个的方案数,后3个除外
    }
    cout<<ans<<endl;
    return 0;
}
2.动态规划
这里的dp[i][k]这里代表从i天开始递增k个数的方案数。
默认递增1个数自己本身为1个,我们理解dp[i][k]等于 数值比a[i]大的且位置比i后的j, 且递增的个数为k-1的方案数累加得到。每次把所有j位置的所得到的不同的递增数的方案转移到i上。
从后往前递推,状态转移方程dp[i][j]= ∑dp[k][j-1] (k>i,a[k]>a[i]),转移过程中当方案数不存在时退出。
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
ll a[2005];
ll dp[2005][5];
ll n,p,t;
int main(){
    cin>>n;
    for(int i=0;i<n;i++)cin>>a[i];
    for(int i=n-1;i>=0;i--){
        dp[i][1]=1;///第一个递增数为自己
        for(int j=i+1;j<n;j++){
            if(a[i]<a[j]){///如果后面的数比i大
                int k=2;///从使递增数为2开始
                while(k<=4){
                    if(dp[j][k-1]==0)break;
                    dp[i][k]+=dp[j][k-1];
                    k++;
                }
            }
        }
    }
    ll ans=0;
    for(int i=0;i<n-3;i++){
        ans+=dp[i][4];
    }
    cout<<ans<<endl;
    return 0;
}
 
原文地址:https://www.cnblogs.com/mohari/p/13546099.html