经典dp模型的变形(未完待续。。)

来源:互联网 发布:域名推广 编辑:程序博客网 时间:2024/04/30 00:24

这几天做了几道dp题,发现有几道是很经典的dp模型的变形,总结一下:

数字三角形

这算是dp的最最最入门的题了吧,但是这题变形的话,还是需要好好想想才能看出来的。
题目:hdu 1176 免费馅饼
题意:

一定时间,天上会从某个位置Xi掉下一块馅饼,1< =xi< =10,这个人位于xi,只能接到xi-1,xi,xi+1,三处之一,问这人可以接到的最多馅饼?

分析:

这题想想还是挺有意思的QAQ,因为是按照时间落馅饼,所以dp的方向是显然的,就是按照时间,第二维当然就是位置了!所以用dp[i][j]表示在i时刻这个人在j位置可以得到的最大馅饼数,num[i][j]表示i时刻j位置落下的馅饼数,不难写出dp方程:
dp[i][x]=max(dp[i-1][x-1],dp[i-1][x],dp[i-1][x+1])+num[i][x]
想明白后,0时刻在5这个位置,然后顺着时间转移,转移到最后一块馅饼落下的时刻,那么这题不就做出来了吗?当然可以!注意下边界即可。
仔细想想这题,这不就是数字三角形吗?所以逆推很容易就可以搞定了!

const int N=1e5+2;int n,x,T,t,f[N][11];int main(){    while(~scanf("%d",&n)&&n){        T=0;        memset(f,0,sizeof(f));        for(int i=0;i<n;i++){            scanf("%d%d",&x,&t);            f[t][x]++;            if(T<t)T=t;        }        for(int i=T-1;i>=0;i--){            for(int j=1;j<=9;j++)                f[i][j]+=max(f[i+1][j+1],max(f[i+1][j],f[i+1][j-1]));            f[i][0]+=max(f[i+1][0],f[i+1][1]);            f[i][10]+=max(f[i+1][10],f[i+1][9]);        }        cout<<f[0][5]<<endl;    }    return 0;}

背包问题

背包问题无疑是变形最多的一类dp题,要想掌握好这类题目,关键还是要深刻理解01背包和完全背包!当然还有更多变形,像分组背包,有依赖的背包,泛化背包等,都是些不好掌握的东东,当然做题时如果能看出这是背包的题目,就离解决问题更近了一大
步!
(此处应有各种背包的分类,等多做几道题后再补QAQ)

完全背包

题目:hdu 1114 Piggy-Bank
题意:

存钱罐里有一些硬币,总共重量是F,空罐重量是E,给出n种钱币,每种钱币的有两个参数,v价值,w重量。问存钱罐中最少的价值是多少?

分析:

赤裸裸的完全背包的题目,注意初始化。

const int INF=0x3f3f3f3f;const int N=1e4+9;int n;int E,F;int f[N],p[N],w[N];int main(){    int T; scanf("%d",&T);    while(T--){        scanf("%d%d",&E,&F);        F-=E;        scanf("%d",&n);        for(int i=1;i<=n;i++){            scanf("%d%d",&p[i],&w[i]);        }        for(int i=1;i<=F;i++)f[i]=INF;        f[0]=0;        for(int i=1;i<=n;i++){            for(int v=w[i];v<=F;v++)                f[v]=min(f[v],f[v-w[i]]+p[i]);        }        if(f[F] == INF)            printf("This is impossible.\n");        else            printf("The minimum amount of money in the piggy-bank is %d.\n",f[F]);    }    return 0;}

最长上升子序列 LIS

最长上升子序列变形是比较多的,可以先看我以前写的这篇:http://blog.csdn.net/hjt_fathomless/article/details/52176146
题目的话,有很多,比如矩形嵌套(按一边排序后,就可以LIS了),叠木块等!
题目:http://blog.csdn.net/hjt_fathomless/article/details/52176513

最长公共子序列 LCS

可以求解最长回文子序列LPS(Longest Palindromic Subsequence)问题
题目:http://blog.csdn.net/hjt_fathomless/article/details/52424136

简单的dp题

简单的dp题是什么东西?就是决策是显然的,一般递推就可以解决,这种题决策都不会太复杂(反正复杂的我也不会做QAQ),而且方向是明显的,所以是简单的。
比如走楼梯那种题,只有两个决策,一步or两步,方向是从0—n(楼底到楼上)。再比如上边所述的数字三角形,决策和方向都是很容易看出来的(所以才是入门题啊)。碰见这种题,那就偷着乐吧!
题目:hdu 1260 Tricks
题意:

售票员有两种选择,一次一票or两票,问花时最少?

分析:

这题是不是很简单,决策是显然的,一票or两票,方向从第一个人到最后一个人楼,这不是跟走楼梯那题一样吗?所以是简单dp!

const int N=1e5+2;int a[N],b[N],f[N],t[5];char c[5];int main(){    int n;    int T;scanf("%d",&T);    while(T--){        scanf("%d",&n);        for(int i=1;i<=n;i++)scanf("%d",&a[i]);        for(int i=2;i<=n;i++)scanf("%d",&b[i]);        f[1]=a[1];        for(int i=2;i<=n;i++)            f[i]=min(f[i-1]+a[i],f[i-2]+b[i]);        t[1]=f[n]/3600; f[n]%=3600;        t[2]=f[n]/60;   f[n]%=60;        t[3]=f[n];        t[1]+=8;        c[1]='a'; c[2]='m';        if(t[1]>12){            t[1]-=12;            c[1]='p';        }        printf("%02d:%02d:%02d %c%c\n",t[1],t[2],t[3],c[1],c[2]);    }    return 0;}
0 0