1. 程式人生 > 實用技巧 >習題:Short Colorful Strip(DP)

習題:Short Colorful Strip(DP)

題目

傳送門

思路

如果一個區間沒有被染色,或者被全部染色

那麼這個區間是很容易被計算的,也就是指我們可以考慮設計這種狀態來計算答案

\(dp[i][j]\)\(i\)\(j\)這個區間沒有被染色或者全部被染色的染成正確方案的方案數

如果當前是第\(i\)種顏色,設其的位置為\(p_i\),那麼\(l\)~\(p_i-1\)\(p_i+1\)\(r\)的位置是可以任意染的

即我們將一個區間分成了4段

\(dp[l][r]=\sum_{k_1=l}^{p_i-1}\sum_{k_2=p_2+1}^{r}dp[l][k_1]*dp[k_1+1][p_i-1]*dp[p_i+1][k_2]*dp[k_2+1][r]\)

很明顯,這個轉移是\(O(n^2)\)

但是我們觀察這個式子,很明顯可以化簡成為

\(dp[l][r]=\sum_{k_1=l}^{p_i-1}dp[l][k_1]dp[k_1+1][p_i-1]*\sum_{k_2=p_i+1}^{r}dp[p_i+1][k_2]dp[k_2+1][r]\)

即轉移就可以變成\(O(n)\)

總共的時間複雜度為$O(n^3) $

程式碼

#include<iostream>
#include<cstring>
using namespace std;
const int mod=998244353;
long long dp[505][505];
//l~r未被染色或者染成同一種顏色之後染成正確顏色的種類數
int n,m;
int a[505];
long long dfs(int l,int r)
{
	if(l>r)
		return 1;
    if(l==r)
        return dp[l][r]=1;
    if(dp[l][r]!=-1)
    	return dp[l][r];
    int _ind=-1;
    int minn=(1<<30);
    for(int i=l;i<=r;i++)
    {
        if(a[i]<minn)
        {
            minn=a[i];
            _ind=i;
        }
    }
    long long s1=0,s2=0;
    for(int len=0;_ind+len<=r;len++)
    	s1=(s1+dfs(_ind+1,_ind+len)*dfs(_ind+len+1,r))%mod;
    for(int len=0;_ind-len>=l;len++)
    	s2=(s2+dfs(_ind-len,_ind-1)*dfs(l,_ind-len-1))%mod;
    dp[l][r]=(max(s1,1ll)*max(s2,1ll))%mod;
    return dp[l][r];
}
int main()
{
	memset(dp,-1,sizeof(dp));
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    cout<<dfs(1,n);    
    return 0;
}