杭电60道DP问题总结(二)

来源:互联网 发布:java向数组中添加元素 编辑:程序博客网 时间:2024/06/03 16:43


接着上一篇文章。

上次一次写的有点长了,保存好半天不成功,这次短一点吧,5道题目。

题目链接地址:http://acm.hdu.edu.cn/problemclass.php?id=516

ProID 1081 To The Max

题目大意是,给一个二维数组,让你求一个和最大的子矩阵。

也算是一道比较简单的题目了,这个除了用暴力法外,比较经典的做法是DP,这个其实也是那个最大子序列和的一个变种。来看看为什么。

比如说求这个矩阵的最大子序列和:

0 -2 -7 0

9 2 -6 2

对于第一行来说,其实就是求一个连续子序列之和,对于两行来说,把他们加起来得到序列 9,0,-13,2,同样也是求最大子序列和。好了问题解决了。

代码如下:

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;int n;int data[101][101];int sumdata[101];int main(){    while(scanf("%d",&n)!=EOF)    {        for(int i=1;i<=n;i++)            for(int j=1;j<=n;j++)                scanf("%d",&data[i][j]);        int maxNum=-100000;        for(int i=1;i<=n;i++)//行递增        {            memset(sumdata,0,sizeof(sumdata));            for(int j=i;j<=n;j++)//高度从i到n的矩阵            {//当前行的所有列相加,然后求最长子序列和,记录最大值                for(int k=1;k<=n;k++)                    sumdata[k]+=data[j][k];                int maxTemp=-100000;                int sum = 0;                for(int k=1;k<=n;k++)                {                    sum += sumdata[k];                    maxTemp = max(sum,maxTemp);                    sum = sum>0?sum:0;                                    }                maxNum=max(maxNum,maxTemp);            }        }        printf("%d\n",maxNum);    }    return 0;}

ProID 1087 Super Jumping! Jumping! Jumping!

题目大意是求一个最长上升子序列,在上一篇文章中已经讲过类似的问题了,不过是用二分查找做的,因为这道题目是要求输出序列之和,就得老老实实用原方法了。

代码如下:

#include<iostream>#include<string>#include<cstdio>#include<vector>#include<cctype>using namespace std;int data[1001];int dp[1001];int tmp[1001];int main(){    int n;    while(scanf("%d",&n)!=EOF,n)    {                memset(dp,0,sizeof(dp));        memset(tmp,0,sizeof(tmp));        for(int i=0;i<n;i++)            scanf("%d",&data[i]);        dp[0]=data[0];        tmp[0]=0;        int sum=0;        for(int i=1;i<n;i++)        {            dp[i]=data[i];            for(int j=0;j<i;j++)            {                if(data[i]>data[j] && dp[i]<dp[j]+data[i])                    dp[i]=dp[j]+data[i];                sum=max(sum,dp[i]);            }        }        printf("%d\n",sum);    }    return 0;}


ProID 1114 Piggy-Bank

题目意思是给定一些硬币,有重量和价值,然后给一个一定重量的罐子,问在能否在装满罐子的条件下,猜到这个罐子的最小价值。这个是完全背包,在背包九讲中有详细的解释。也只是套个公式而已,需要注意初始化问题。

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;int t;int dp[10005];struct _data{    int a,b;};_data data[501];int main(){    while(scanf("%d",&t)!=EOF)    {        int E,F,n;        while(t--)        {            scanf("%d %d",&E,&F);            scanf("%d",&n);            memset(dp,0,sizeof(dp));            bool flag=false;            for(int i=0;i<n;i++)            {                scanf("%d %d",&data[i].a,&data[i].b);                if(data[i].b<F-E)                    flag=1;            }            if(!flag)                printf("This is impossible.\n");            else            {            //初始化和01背包不一样                for(int i=1;i<=F-E;i++)                    dp[i]=5000000;//完全背包问题dp[i][j]表示考虑前i个物品在重量为j的情况下得到的最小价值                for(int i=0;i<n;i++)                {                    for(int j=data[i].b;j<=F-E;j++)                    {                        dp[j]=min(dp[j],dp[j-data[i].b]+data[i].a);                    }                }                                if(dp[F-E]!=5000000)                    printf("The minimum amount of money in the piggy-bank is %d.\n",dp[F-E]);                else                    printf("This is impossible.\n");            }        }    }    return 0;}

ProID 1121 Complete the Sequence

题目大意是,给顶你一个符合P(n)=P(n) = aD.n^D+aD-1.n^D-1+...+a1.n+a0 多项式的序列,然后让你续填m个序列,说白了就是找出一个序列的规律来,就是按规律填数。

咋一看之下好像无从下手,a(*)怎么找啊,n如何确定?其实吧多项式相减就可以看出规律来,因为这个多项式是比较简单的相邻数相减就可消去高次方,然后再减可以消去下一个高次方。例子如下:

对于序列  1,2,3,4,7来说,012471123--211----30------

表格中描述的是3次相减之后的情况,总是可以在最后一步(也就是第n-1次相减中变为一个数字)现在来计算序列之后的数字,从下往回推:

300------2111----11234--0124711来看看表格中第3行第3列的2是如何推导出来的,记住了差值是连续的两项的差,所以2=第二行第二列+第三行第2列的值,所以最后的11=第3行第5列的4+第四行第5列的7。

代码如下:

#include<iostream>#include<cstdio>#include<algorithm>using namespace std;int n,m,t,s[105][105];int main(){    while(scanf("%d",&t)!=EOF)    {        while(t--)        {            scanf("%d %d",&n,&m);            for(int i=0;i<n;i++)                scanf("%d",&s[0][i]);            for(int i=1;i<n;i++)//计算n阶差值                for(int j=0;i+j<n;j++)                    s[i][j]=s[i-1][j+1]-s[i-1][j];            for(int i=1;i<=m;i++)                s[n-1][i]=s[n-1][0];//还原过程            for(int i=n-2;i>=0;i--)                for(int j=0;j<m;j++)                    s[i][n-i+j]=s[i][n-i+j-1]+s[i+1][n-i+j-1];//输出m需要求的值            for(int i=0;i<m-1;i++)                printf("%d ",s[0][n+i]);            printf("%d\n",s[0][n+m-1]);        }            }return 0;}

ProID  1158 Employment Planning

题目大意是老板要雇一群人工作,雇人和解雇都要付工资,工作的时候要付薪水而且不工作也要付(真好啊 )。问你帮忙计算出完成一项工作所需的最小花费。

这道题目很类似于前一篇文章中的星河战队问题,只不过这个比那道题目稍微简单一些,因为是线性的,不是树形的。

状态转移方程如下:

  Dp[i][j]为前i个月的留j个人的最优解;  其中 Num[i]<=j<=Max{Num[i]};  Num 数组表示的是当前这个月所需的最少人数,当然如果所雇用的人数绝对不超过所有月份中所多的人数,否则白白养那么一群人,老板也太好了。
对于Num[i]<=k<=Max_n,    当k<j时, 招聘;当k>j时, 解雇  然后求出最小值。Dp[i][j]=min{Dp[i-1][k…Max_n]+(招聘,解雇,工资);

代码如下:

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;int dp[13][1005],num[13];int n;int hire,fire,salary;  int main(){     while(scanf("%d",&n),n)     {         int MaxNum=0;         scanf("%d%d%d",&hire,&salary,&fire);//如果说其中某个月所需要的人最多,那么总的人数不会超过这个,否则就是这个老板太慈善了。         for(int i=0;i<n;i++)         {             scanf("%d",&num[i]);             MaxNum=max(MaxNum,num[i]);         }         memset(dp,0,sizeof(dp));         for(int i=num[0];i<=MaxNum;i++)         {             dp[1][i]=i*(salary+hire); //hire 也要加上         }         for(int i=2;i<=n;i++)         {             for(int j=num[i-1];j<=MaxNum;j++)             {                  int Min=0xffffff;                  for(int k=num[i-2];k<=MaxNum;k++)//上个月雇佣的人数                  {//当k<j时, 招聘, 当k>j时, 解雇,  然后求出最小值                      if(k>j)                          Min=min(Min,dp[i-1][k]+(k-j)*fire+j*salary);                      else                           Min=min(Min,dp[i-1][k]+(j-k)*hire+j*salary);                  }                  dp[i][j]=Min;             }         }         int ans=0xffffff;          for(int i=num[n-1];i<=MaxNum;i++)               ans=min(ans,dp[n][i]);          printf("%d\n",ans);     }     return 0;}


原创粉丝点击