低價購買【lcs變形/dp】

在這裏插入圖片描述
這不是一道簡單的求最長下降子序列的問題。因爲我們還需要求不相同的方案數目,而求方案數目也是一個dp的過程。

我們使用dp[i]dp[i]來表示到以ii個元素結尾的最長下降子序列的長度,c[i]c[i]表示以ii個元素結尾的最長下降子序列的組合數目,a[i]a[i]表示第ii個元素。那麼我們可以得到如下的性質

  • 如果dp[i]=1dp[i]=1,那麼c[i]=1c[i]=1,當然也可能有重複元素,極端一點比如1,1,1,1,1,我們最終的結果的組合數要是1,所以這道題的一大難點在於如何去重。
  • 考慮dp[i]=dp[j]+1dp[i]=dp[j]+1,也就是說以ii元素結尾的序列是從以jj結尾的序列派生出來的,那麼我們有c[i]+=c[j]c[i]+=c[j]jj有多少種組合數,我們就能相應的生成多少種ii
  • 如果一個數列的第一個數與另一個數列的第一個數相同,那麼現在可以判斷它們相等,即可以把其中一個刪掉,即c[i]=0c[i]=0,當不同的數接在它的後面時,又可以將它們判斷爲兩個數列,這是不互相影響的。因爲兩個數列都可以由這個相等的數列轉移而來。也就是說我們總是在第一個元素的位置考慮去重,排除所有一開始就相等的元素,那麼之後接上的序列總是不相等的。
		if (a[i] < a[j] && dp[j] + 1 == dp[i])
			c[i] += c[j];//i的組合數和j有關
		else if (dp[i] == dp[j] && a[i] == a[j])
			c[i] = 0;//去重,避免完全相同的方案
#define ll long long 
#define inf 0x3ffffff
#define MAX 5005
#define vec vector<ll>

int main() {
	ll N, a[MAX], dp[MAX], c[MAX];
	while (cin >> N) {
		memset(dp, 0, sizeof(dp));
		memset(c, 0, sizeof(c));
		ll maxx = 0, cnt = 0;
		for (int i = 1; i <= N; i++)cin >> a[i];
		for (int i = 1; i <= N; i++) {
			dp[i] = 1;
			for (int j = i - 1; j >= 1; j--) {
				if (a[i]<a[j] && dp[j] + 1 > dp[i])
					dp[i] = dp[j] + 1;
			}
			if (dp[i] == 1)c[i] = 1;//不能組成序列,只有一種組合數
				//注意這個方向是從1-i,而不能從i-1
			for (int j = 1; j < i; j++) {
				if (a[i] < a[j] && dp[j] + 1 == dp[i])
					c[i] += c[j];//i的組合數和j有關
				else if (dp[i] == dp[j] && a[i] == a[j])
					c[i] = 0;//去重,避免完全相同的方案
			}

			if (dp[i] > maxx)maxx = dp[i];
		}
		for (int i = 1; i <= N; i++)
			if (dp[i] == maxx)cnt += c[i];
		cout << maxx << ' ' << cnt << endl;
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章