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

这几天做了几道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

我记得以前在bestcoder上做过一道很好的变形题,不过忘了QAQ。等看到了再补吧!
不过重要的还是记住状态转移方程,知道怎么得到的!

简单的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;
}
posted @ 2016-08-12 16:13  HARD_UNDERSTAND  阅读(1049)  评论(0编辑  收藏  举报