區間dp 小小結

A - Halloween Costumes LightOJ - 1422

題意:有n個萬聖節晚會,晚會按順序參加,每個晚會都要求穿要求的衣服。衣服上面可以套衣服。問穿衣服的最少次數

分析:解釋一下樣例,1 2 1 2(4個晚會的衣服),可以先穿1,然後穿2,脫2,穿2。所以穿衣服的次數是3次。

區間dp。
狀態 : dp[i][j]:第i個晚會到第j個晚會穿衣服的最少次數。
開始是這樣想的,假設i的衣服在k的位置重複利用了那就是
for(int k=i+1;k<=j-1;k++)
if(a[i]==a[k])
dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]);
但是wa了,因爲對於dp[i][j]來說,我們只考慮i的重複出現,但是j可能也是重複出現的呀
然後加一個
for(int k=i+1;k<=j-1;k++)
if(a[j]==a[k])
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j-1]);
就過了。其實這兩個可以合成一個
for(int k=i;k<=j-1;k++)
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>

using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
inline int MIN(int a,int b) {return a<b?a:b;}
inline int MAX(int a,int b) {return a>b?a:b;}

const int maxn = 105;
int a[maxn],dp[maxn][maxn];
int main()
{
    int T,n,case1=1;
    scanf("%d",&T);
    while(T--)
    {
        mem(dp,inf);
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int len=1;len<=n;len++)
        {
            for(int i=1;i<=n;i++)
            {
                int j=i+len-1;
                if(j>n) break;
                if(len==1) {dp[i][j]=1;continue;}
                else if(a[j-1]==a[j]) dp[i][j]=min(dp[i][j],dp[i][j-1]);
                if(a[i]==a[j]) {dp[i][j]=min(dp[i][j],dp[i][j-1]);}
                for(int k=i;k<=j-1;k++)
                        dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
            }
        }
        printf("Case %d: %d\n",case1++,dp[1][n]);
    }
    return 0;
}
/*
2
7
1 2 1 1 3 2 1
*/

B - Brackets POJ - 2955

題意:括號匹配。給你一個字符串,有四種字符’(‘,’)’,’[‘,’]’,(),[]這樣算一對,問這串字符串中最多有多少匹配字符。

什麼是匹配,就是對裏面是對。比如([]),有四個匹配字符,([)],這樣只有兩個匹配字符。

分析 :
狀態 dp[i][j] :從下標i到j的最大匹配字符的數量
狀態轉移:dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>

using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
inline int MIN(int a,int b) {return a<b?a:b;}
inline int MAX(int a,int b) {return a>b?a:b;}

const int maxn = 105;
char s[maxn];
int dp[maxn][maxn];
int main()
{
    while(scanf("%s",s)!=EOF)
    {
        if(s[0]=='e') break;
        mem(dp,0);
        int slen=strlen(s);
        for(int len=2;len<=slen;len++)
        {
            for(int i=0;i<slen;i++)
            {
                int j=i+len-1;
                if(j>=slen) break;
                else if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']')) {if(len==2) dp[i][j]=2;else dp[i][j]=max(dp[i][j],dp[i+1][j-1]+2);}
                for(int k=i;k<=j-1;k++)
                        dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);
            }
        }
        printf("%d\n",dp[0][slen-1]);
    }
    return 0;
}
/*
()()()()
*/

C - Coloring Brackets CodeForces - 149D

題意:給一串匹配字符串,字符只有’(’ ‘)’ ,所以字符的對應是唯一的。給這些字符塗色,有下面幾點要求
1.有兩種顏色,紅色,藍色。開始的括號是無色的。
2. 每對括號,只能有一個括號被塗色(紅或藍)
3.相鄰兩個字符不能塗相同顏色,可以同時不塗色。
問一共有多少種塗色方案,方案數mod 1e9+7
分析;首先,模擬一下棧,記錄一下字符的對應。然後區間dp
dp[l][r][i][j]:下標從l到r,l顏色爲i,r顏色爲j的方案數

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>

using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn = 705,inf=1e9+7;
char s[maxn];
ll dp[maxn][maxn][5][5];
int mat[maxn],tem[maxn];
void getmat(int len)
{
    int p=0;
    for(int i=0;i<len;i++)
    {
        if(s[i]=='(')
            tem[p++]=i;
        else {
            mat[i]=tem[p-1];
            mat[tem[p-1]]=i;
            p--;
        }
    }
}
void dfs(int l,int r)
{
    if(r-l==1)
    {
        dp[l][r][0][1]=1;
        dp[l][r][0][2]=1;
        dp[l][r][1][0]=1;
        dp[l][r][2][0]=1;
        return;
    }
    if(mat[l]==r)
    {
        dfs(l+1,r-1);
        for(int i=0;i<3;i++)
        {
            for(int j=0;j<3;j++)
            {
                if(j!=1)
                    dp[l][r][0][1]=(dp[l][r][0][1]+dp[l+1][r-1][i][j])%inf;
                if(j!=2)
                    dp[l][r][0][2]=(dp[l][r][0][2]+dp[l+1][r-1][i][j])%inf;
                if(i!=1)
                    dp[l][r][1][0]=(dp[l][r][1][0]+dp[l+1][r-1][i][j])%inf;
                if(i!=2)
                    dp[l][r][2][0]=(dp[l][r][2][0]+dp[l+1][r-1][i][j])%inf;
            }
        }
    }
    else {
        int p=mat[l];
        dfs(l,p);
        dfs(p+1,r);
        for(int i=0;i<3;i++)
        {
            for(int j=0;j<3;j++)
            {
                for(int k=0;k<3;k++)
                {
                    for(int h=0;h<3;h++)
                    {
                        if(k==1&&h==1) continue;
                        if(k==2&&h==2) continue;
                        dp[l][r][i][j]=(dp[l][r][i][j]+dp[l][p][i][k]*dp[p+1][r][h][j]%inf)%inf;
                    }
                }
            }
        }
    }
}
int main()
{
    while(scanf("%s",s)!=EOF)
    {
        mem(dp,0);mem(mat,0);
        int slen=strlen(s);
        getmat(slen);
        dfs(0,slen-1);
        ll ans=0;
        for(int i=0;i<3;i++)
            for(int j=0;j<3;j++)
                ans=(ans+dp[0][slen-1][i][j])%inf;
        printf("%lld\n",ans);
    }
    return 0;
}
/*
(())
(()())
*/

D - Multiplication Puzzle POJ - 1651

題意:給一串數字,要將這些數字拿走(除了左右兩邊的數),每拿走一個數,都會有一個價值 ,比如 a1,a2,a3,拿走a2的價值爲a1*a2*a3,問拿走這些數字最少的價值之和

分析:
dp[i][j]:拿走i,j之間的數最少的價值之和
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+a[k]*a[i]*a[j]);

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>

using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn = 105,inf=0x3f3f3f3f;
ll dp[maxn][maxn],a[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    mem(dp,inf);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int len=1;len<=n;len++)
    {
        for(int i=1;i<=n;i++)
        {
            int j=i+len-1;
            if(j>n) break;
            if(len<=2)dp[i][j]=0;
            for(int k=i+1;k<j;k++)
                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+a[k]*a[i]*a[j]);
        }
    }
    printf("%lld\n",dp[1][n]);
    return 0;
}
/*
6
10 1 50 50 20 5
*/

E - You Are the One HDU - 4283

題意:有n個人上臺,每個人都有一個單位怒氣值,如果第k個上臺的人,單位怒氣值爲q,那麼他的怒氣值爲(k-1)*q。這n個人排好隊準備上臺了,舞臺旁邊有一個棧,我們可以通過這個棧來改變,他們上臺順序。問這n個人上臺的怒氣值之和最小爲多少。

分析:
dp[i][j]:表示第i個人到第j個人,最小的怒氣和

dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]-sum[k]+sum[i]+a[i]*(k-1));
表示第i個人,第k個上場。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>

using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn = 105,inf=0x3f3f3f3f;
ll dp[maxn][maxn],a[maxn],sum[maxn];

int main()
{
    int T,n,case1=1;;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        mem(dp,inf);mem(sum,0);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }
        for(int len=1;len<=n;len++)
        {
            for(int i=1;i<=n;i++)
            {
                int j=i+len-1;
                if(j>n) break;
                if(len==1) dp[i][j]=a[i]*(i-1);
                else if(len==2) {dp[i][j]=min(dp[i][j],min(dp[i][i]+dp[j][j],dp[j][j]-a[j]+dp[i][i]+a[i]));}
                else {
                    for(int k=i+1;k<j;k++)
                        dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]-sum[k]+sum[i]+a[i]*(k-1));
                    dp[i][j]=min(dp[i][j],min(dp[i][i]+dp[i+1][j],dp[i+1][j]-sum[j]+sum[i]+a[i]*(j-1)));
                }
            }
        }
        printf("Case #%d: %d\n",case1++,dp[1][n]);
    }
    return 0;
}

F - String painter HDU - 2476

題意:給兩個等長的字符串a,b。有一個操作可以將一段區間的字符全都變成一種任意字符。想將a字符串轉化成b字符串,問最小操作次數。

分析:開始想直接將a轉化成b的,發現對一個區間的改變兩個字符串很難進行判斷,轉化。。

然後dalao們的做法是。

先將空字符串轉化成b的操作數區間dp,然後藉助這個dp來得出a的轉化。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>

using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn = 105,inf=0x3f3f3f3f;
int dp[maxn][maxn],ans[maxn];
char a[maxn],b[maxn];

int main()
{
    while(scanf("%s",a)!=EOF)
    {
        scanf("%s",b);
        mem(dp,inf);mem(ans,inf);
        int slen=strlen(a);
        for(int len=1;len<=slen;len++)
        {
            for(int i=0;i<slen;i++)
            {
                int j=i+len-1;
                if(len==1) dp[i][j]=1;
                else if(b[i]==b[j]) {if(len==2) dp[i][j]=1;else dp[i][j]=min(dp[i][j],min(dp[i+1][j],dp[i][j-1]));}
                for(int k=i;k<j;k++)
                    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
            }
        }
        ans[0]=(a[0]==b[0]?0:1);
        for(int i=1;i<slen;i++)
        {
            ans[i]=dp[0][i];
            if(b[i]==a[i]) ans[i]=min(ans[i],ans[i-1]);
            for(int k=0;k<i;k++)
                ans[i]=min(ans[i],ans[k]+dp[k+1][i]);
        }
        printf("%d\n",ans[slen-1]);
    }
    return 0;
}
/*
zzzzzfzzzzz
abcdefedcba
abababababab
cdcdcdcdcdcd
*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章