【POJ1952】逢低吸纳 dp+计数

题目大意:给定一个有 N 个数的序列,求其最长下降子序列的长度,并求出有多少种不同的最长下降子序列。(子序列各项数值相同视为同一种)

update at 2019.4.3
题解:求最长下降子序列本身并不难,是一道非常经典的线性dp问题,关键在于dp计数部分。这道题跟一般的状态转移计数不同,这里并不是按照状态去计数,对于状态来说不会有重复的情况发生。
考虑何时会产生答案重叠。对于序列中两个值相同的元素 (a_i,a_j,(j<i)),到 i 之前的序列被这两个相同的元素分成了两部分,即:小于 j 的序列(第一部分)和大于 j 小于 i 的序列(第二部分)。下面分情况进行讨论。

  1. 第二部分对 dp[i] 没有产生任何贡献,即:第二部分的元素值都小于 a[i],此时发现 a[i] 和 a[j] 完全等价,因此 i 的答案贡献应该为 0。
  2. 第二部分对 dp[i] 产生了贡献,且 dp[i]>dp[j],这时 i 和 j 的序列显然不会有重叠的概念,毕竟都不一样长嘛qwq。
  3. 第二部分对 dp[i] 产生了贡献,且 dp[i]=dp[j],比如序列 1 3 4 2 4,这时,对于第一个 4 之前的答案种数对 i 忽略不计即可,不过第二部分对 i 的种数贡献需要计入 f[i]。

代码如下

#include <bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) x.begin(),x.end()
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int dx[]={0,1,0,-1};
const int dy[]={1,0,-1,0};
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const int maxn=5010; 
const double eps=1e-6;
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
inline ll sqr(ll x){return x*x;}
inline ll read(){
	ll x=0,f=1;char ch;
	do{ch=getchar();if(ch=='-')f=-1;}while(!isdigit(ch));
	do{x=x*10+ch-'0';ch=getchar();}while(isdigit(ch));
	return f*x;
}
/*--------------------------------------------------------*/

int n,a[maxn];
int dp[maxn],f[maxn],ans1,ans2;

void read_and_parse(){
	n=read();
	for(int i=1;i<=n;i++)a[i]=read();
}

void solve(){
	for(int i=1;i<=n;i++){
		dp[i]=1;
		for(int j=1;j<i;j++)if(a[j]>a[i])dp[i]=max(dp[i],dp[j]+1);
		ans1=max(ans1,dp[i]);
	}
	for(int i=1;i<=n;i++){
		if(dp[i]==1)f[i]=1;
		for(int j=1;j<i;j++)
			if(a[j]>a[i]&&dp[i]==dp[j]+1)f[i]+=f[j];
			else if(a[i]==a[j]&&dp[i]==dp[j])f[i]=0;
		if(dp[i]==ans1)ans2+=f[i];
	}
	printf("%d %d
",ans1,ans2);
}

int main(){
	read_and_parse();
	solve();
	return 0;
}

原文地址:https://www.cnblogs.com/wzj-xhjbk/p/9965360.html