Codeforces - 149D 不錯的區間DP

題意:有一個字符串 s. 這個字符串是一個徹底匹配的括號序列.在這個徹底匹配的括號序列裏,每一個括號都有一個和它匹配的括號
你如今能夠給這個匹配的括號序列中的括號染色,且有三個要求:
每一個括號只有三種狀況,不上色,上紅色,上藍色.
每對括號必須只能給其中的一個上色,且必須給一個上色
相鄰的兩個不能上同色,能夠都不上色
求知足條件的括號序列染色的方法數c++

假設不染色爲0,另外兩種色爲1,2
那對於一個匹配的括號對來講,只容許(1,0),(2,0),(0,1),(0,2)
定義\(f[l][r][i][j]\):區間\([l,r]\)的左端點狀態爲\(i\),右端點狀態爲\(j\)時的當前區間方案數
能夠得出當\(l+1=r\)時,只有上述四種存在方案數1ubuntu

除此之外還有兩種狀況
1.當前處理區間\([l,r]\)兩端點剛好是一個匹配括號對
2.當前處理區間\([l,r]\)兩端點不是一個匹配括號對spa

狀況1能夠先遞歸處理\([l+1,r-1]\)的方案,最後合併方案數,這裏要注意相鄰位置不可取同等顏色
狀況2也可分治\([l,match[l]]\)(狀況1)和\([match[l]+1,r]\)兩個區間處理.方案數是兩個區間合法方案的乘積,一樣要注意約束條件code

具體的操做看代碼遞歸

#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define rrep(i,j,k) for(int i=j;i>=k;i--)
#define println(a) printf("%lld\n",(ll)(a))
#define printbk(a) printf("%lld ",(ll)(a))
typedef long long ll;
using namespace std;
const int MAXN = 732+11;
const ll oo = 0x3f3f3f3f3f3f3f3f;
const ll ooo= 0x3f3f3f3f;
const int MOD = 1e9+7;
ll read(){
   ll x=0,f=1;register char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
   while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
   return x*f;
}
ll dp[MAXN][MAXN][3][3];
int match[MAXN];
char str[MAXN];
void DP(int l,int r){
    if(l==r){
        rep(i,0,2) rep(j,0,2) dp[l][r][i][j]=0;
        return;
    }
    if(l+1==r){
        dp[l][r][0][0]=0;
        dp[l][r][1][1]=0;
        dp[l][r][2][2]=0;
        dp[l][r][1][2]=0;
        dp[l][r][2][1]=0;
        
        
        dp[l][r][1][0]=1;
        dp[l][r][0][1]=1;
        dp[l][r][2][0]=1;
        dp[l][r][0][2]=1;
    }else if(match[l]==r){
        DP(l+1,r-1);
        rep(i,0,2) rep(j,0,2) dp[l][r][i][j]=0; 
        rep(i,0,2) rep(j,0,2){ // i==j ok/ err 0
            if(j!=1) dp[l][r][0][1]+=dp[l+1][r-1][i][j];
            if(j!=2) dp[l][r][0][2]+=dp[l+1][r-1][i][j];
            if(i!=1) dp[l][r][1][0]+=dp[l+1][r-1][i][j];
            if(i!=2) dp[l][r][2][0]+=dp[l+1][r-1][i][j];
            rep(x,0,2) rep(y,0,2){
                if(dp[l][r][x][y]>=MOD) dp[l][r][x][y]%=MOD;
            }
        }
    }else{
        DP(l,match[l]); DP(match[l]+1,r);
        rep(i,0,2) rep(j,0,2) dp[l][r][i][j]=0;
        rep(i,0,2) rep(j,0,2) rep(x,0,2) rep(y,0,2)if((x==y&&x==0)||(x!=y)){
            dp[l][r][i][j]+=dp[l][match[l]][i][x]*dp[match[l]+1][r][y][j]%MOD;
            if(dp[l][r][i][j]>=MOD) dp[l][r][i][j]%=MOD;
        }
    }
} 
int main(){
    while(~scanf("%s",str+1)){
        memset(dp,-1,sizeof dp);
        stack<int> stk;
        int n=strlen(str+1);
        rep(i,1,n){
            if(str[i]=='(') stk.push(i);
            else{
                int pos=stk.top();stk.pop();
                match[pos]=i;
            }
        }
        DP(1,n);
        ll res=0;
        rep(i,0,2) rep(j,0,2) res=(res+dp[1][n][i][j])%MOD;
        println(res);
    }
    return 0;
}

Codeforces - 1025D

題意:給出n個節點的值\(a_i\),若兩節點的值gcd不爲1則可連邊,問這n個點可否構成一個二叉樹字符串

//壓根沒想到是區間DPget

\(L[l][r]\):以\(r\)爲根,\([l,r-1]\)可否構成左子樹
\(R[l][r]\):以\(l\)爲根,\([l+1,r]\)可否構成右子樹
\(ok[l][r]\):\([l,r]\)可否構成二叉樹it

用區間DP的方法不斷擴展l,r可行範圍便可
具體畫個圖很容易看出來左右的擴展方式ast

https://paste.ubuntu.com/p/VdBdSjMYYR/class


598E

題意:一個大小爲n*m的巧克力,要分出面積k的巧克力(能夠拼湊出來),每次操做只能橫着掰開或豎着掰開,代價爲掰開的邊長的平方,求最小代價

思緒良久,仍是\(O(n^5)\)暴力出全部狀況

https://paste.ubuntu.com/p/7QJcxg8SFc/


245H

題意:求出全部\([L,R]\)的迴文子串個數

容斥定理,對於\([L,R]\)內的貢獻,來自於\([L,R-1]\)\([L+1,R]\),重複貢獻爲\([L+1,R-1]\)
若是\(str_L=str_R\)且內部爲迴文串,那麼貢獻要+1(SB如我又加了一遍[L+1,R-1])

https://paste.ubuntu.com/p/4S35zpYzRz/


159D

題意:求\(s[a,b]\) && \(s[x,y]\)的迴文子串對的個數,\(1≤a≤b<x≤y≤n\)

感受和前面的題目很類似,但答案並不是\(dp[1][i]*dp[i+1][n]\),這樣存在重複貢獻

所以引入一個\(g[i]\):以\(i\)結尾的迴文串個數

這樣就能夠\(g[i]*dp[i+1][n]\)不重不漏地求出全部數量

https://paste.ubuntu.com/p/5TjgK8kX6R/


Gym - 101196F

題意:給出一個\(a[1...n]\)的環,n接回1,環中每刪除一個數的代價是數兩旁元素的gcd值,求最小刪除代價

\(dp[i][j]\):\([i,j]\)區間只剩\(i\)\(j\)的最小代價

而後xjb亂寫竟然過了?

https://paste.ubuntu.com/p/k25HZxxcCQ/

相關文章
相關標籤/搜索