BZOJ 1996 合唱隊 - 區間DP(以及DP注意事項)

很快看出來是區間dp,搞了個O(n^3)的dp方程,0/1狀態表示當前區間的首/尾作爲末尾,然後搞記憶化搜索半天沒搞出來,還得搞個RMQ,於是百度了題解(爲毛題解都長的一樣,還都是WA的。。。這羣人抄也不抄仔細點。。。)

題解大概就是通過一個個數插入將區間向兩邊擴大,而且只能放在兩頭,條件就是上一個放的數值和這個數的數值滿足題意。討論一下就好了,dp方程寫出來還是很簡單的。(其實就是將我的思路改進一下,不合並區間與區間,合併區間與數)


這裏需要注意枚舉區間i,j的方向,由於i+1向i拓展,j-1向j拓展,於是i+1應該在i之前,於是i指針應該倒序,同理j應該升序。

注意狀態轉移的方向和枚舉的方向是相反的!


其次就是初值,觀察列出的dp方程,發現如果j==i+1時,dp[i][i]和dp[j][j]會向dp[i][j]加兩遍(因爲dp[i][i]無序),所以初值只任意設一個就好了。


還有就是必須得吐槽一下網上的代碼,照着寫了半天,交上去WA了一屏。結果把網上的原碼交上去照樣WA。爆int是要鬧哪樣啊。。。一份兩份全都爆int是要鬧哪樣啊(霧)。。。


#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>

using namespace std;

const int maxn=1005;
const int mod=19650827;

int n;
int s[maxn];
long long dp[maxn][maxn][2];

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",s+i),
		dp[i][i][1]=1;//只選0/1中一個賦值 
	for(int i=n;i;i--)
		for(int j=i+1;j<=n;j++)
		{
			if(s[i]<s[j])dp[i][j][1]+=dp[i][j-1][0];
			if(s[i]<s[j])dp[i][j][0]+=dp[i+1][j][1];
			if(s[i+1]>s[i])dp[i][j][0]+=dp[i+1][j][0];
			if(s[j]>s[j-1])dp[i][j][1]+=dp[i][j-1][1];
			dp[i][j][0]%=mod;dp[i][j][1]%mod;
		}
	printf("%d",(dp[1][n][0]+dp[1][n][1])%mod);
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章