区间dp 小小结

来源:互联网 发布:传统武术实战 知乎 编辑:程序博客网 时间:2024/06/05 23:51

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;}/*271 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;}/*610 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;}/*zzzzzfzzzzzabcdefedcbaababababababcdcdcdcdcdcd*/
原创粉丝点击