P1108 低价购买 - DP - 基于dp数组求方案

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Fantasy_World/article/details/82788463

首先求一遍最长下降子序列
然后考虑到方案会重复,而避免重复是件挺难的事,不妨先考虑不管重复求方案

那么如何求方案呢,设pl(i)表示以i为结尾的最长下降子序列方案数,因为表示的是最长的序列,所以只有在i前面,比i大,最长序列长度还恰好比i小1的可以方案转移到i上

然后删掉那些重复的,但是重复的实在难以判断,但是仔细想想也许不必删除,只需要消除某些点的影响就好了
我们在统计方案的时候,是一个转移,但是可能会从等效的位置转移到同一个位置上,因此考虑把等效的位置去掉只留下一个代表位置
当ai == aj , i > j,并且我们已经统计完前i个位置的方案时,ai完全可以代替aj了,因为子序列这个东西是能“跨越”的,所以把pl(aj)设为0,这样aj就不会对以后的位置方案做出贡献,统计之后的位置时,所有包含aj的子序列都能转化为包含ai且完全相同的子序列

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 5000 + 10;
const int MAXM = MAXN * 2;
int n,a[MAXN],ans,plan,f[MAXN],pl[MAXN];
int main() {
    scanf("%d", &n);
    for(int i=1; i<=n; i++) {
        scanf("%d", &a[i]);
        f[i] = 1;
        
    }
    for(int i=1; i<=n; i++) {
        for(int j=1; j<i; j++) {
            if(a[j] > a[i]) {
                f[i] = max(f[i], f[j]+1);
            }
        }
        ans = max(ans, f[i]);
    }
    for(int i=1; i<=n; i++) {
        if(f[i] == 1) pl[i] = 1;
        for(int j=1; j<i; j++) {
            if(a[j] > a[i] && f[i] == f[j] + 1) {
                pl[i] += pl[j];
            } else if(a[i] == a[j]) {
                pl[j] = 0;
            }
        }
    }
    for(int i=1; i<=n; i++) {
        if(f[i] == ans) plan += pl[i];
    }
    printf("%d %d", ans, plan);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Fantasy_World/article/details/82788463
今日推荐