基础DP1 HDU1024+HDU 1074

HDU 1074

链接:http://acm.hdu.edu.cn/showproblem.php?pid=1024

题意:给定一个数组求其分成m个不相交的子段和最大问题;

做法(这里是看kuangbin的做法的)


设Num为给定数组,n为数组中的元素总数,Status[i][j]表示前i个数在选取第i个数的前提下分成j段的最大值,其中1<=j<=i<=n && j<=m,状态转移方程为:
Status[i][j]=Max(Status[i-1][j]+Num[i],Max(Status[0][j-1]~Status[i-1][j-1])+Num[i])
乍看一下这个方程挺吓人的,因为题中n的限定范围为1~1,000,000而m得限定范围没有给出,m只要稍微大一点就会爆内存。但仔细分析后就会发现Status[i][j]的求解只和Status[*][j]与Status[*][j-1]有关所以本题只需要两个一维数组即可搞定状态转移。
在进行更进一步的分析还会发现其实Max(Status[0][j-1]~Status[i-1][j-1])根本不需要单独求取。在求取now_Status(保存本次状态的数组)的过程中即可对pre_Status(保存前一次状态的数组)进行同步更新。

注意:这里的做法有点像背包的01数组,但这里更巧妙的是,在每次滚动的时候顺便将j-1的前i-1的最大值求出。就可以直接转移了

具体操作;

dp[i]=max(dp[i-1]+a[i],mmdp[i-1]+a[i]);
               mmdp[i-1]=ans;
               ans=max(ans,dp[i]);


#include 
#include
#include
#include
using namespace std;
int a[1000010];
int dp[1000010];
int mmdp[1000010];
int main()
{
    int n,m;
    while(scanf("%d%d",&m,&n)!=EOF)
    {
        int ans;
        for(int i=1;i<=n;++i)
        {
            dp[i]=0;
            mmdp[i]=0;
            scanf("%d",&a[i]);
        }
        dp[0]=mmdp[0]=0;
        for(int j=1;j<=m;++j)
        {
           ans=-0x3fffffff;
           for(int i=j;i<=n;++i)
           {
               dp[i]=max(dp[i-1]+a[i],mmdp[i-1]+a[i]);
               mmdp[i-1]=ans;
               ans=max(ans,dp[i]);
           }
        }
        printf("%d\n",ans);
    }
    return 0;
}


HDU:1074
http://acm.hdu.edu.cn/showproblem.php?pid=1074

题意:给出一些作业,每种作业有两个参数(截止日期,完成作业需要多久)每超过截止日期一天扣一分,问最少扣多少分,如果答案相同输出字典序最小的.(题目的输入顺序是字典序从小到大)

傻傻的贪心贪了特别久,各种排序呜呜呜~~~~(>_<)~~~~

看了题解:发现尼玛。。。状压DP完全没想过。

思路:一个n位的二进制数,每一位表示第几个作业是否做完.然后对于每一个数枚举位数,判断是否能将扣的分变小(有点像松弛操作).枚举的时候倒叙枚举这样才字典序最小.同时每次松弛之后要把pre记录下来方便后面的输出!

#include 
#include
#include
#include
using namespace std;
const int maxn=1<<15;
struct node
{
    char name[110];
    int dead,need;
}home[30];
struct DP
{
    int cost;
    int time;
    int pre;
}dp[maxn+10];

void dfs(int n)
{
    if(!n) return;
    int x=dp[n].pre;
    dfs(n-(1<=0;--j)
            {
                int temp=1<dp[i-temp].cost+reduce)
                    {
                        dp[i].cost=dp[i-temp].cost+reduce;
                        dp[i].pre=j;
                        dp[i].time=dp[i-temp].time+home[j].need;
                    }
                }
            }
        }
        printf("%d\n",dp[k-1].cost);
        dfs(k-1);
    }
    return 0;
}


HDU 1176

链接:http://acm.hdu.edu.cn/showproblem.php?pid=1176

中文题不解释了。

自己做的时候是正向考虑的也是用dp,因为每次都要判断这个点是否能到达,还有边界情况然后就写炸了.看了别人的博客,都是从结尾往回dp,这样就不需要再去找答案,且不需要考虑这个点能不能走到,或者什么时候能走到的问题。边界的问题直接把坐标左移1,就可以了(哦,我真是蠢)!!.

#include 
#include
#include
#include
using namespace std;
const int maxn=100005;
int dp[15][maxn];
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0) break;
        memset(dp,0,sizeof(dp));
        int maxtime=0;
        for(int i=0;i=0;--j)
        {
            for(int i=1;i<=11;++i)
            {
                dp[i][j]+=max(dp[i][j+1],max(dp[i-1][j+1],dp[i+1][j+1]));
            }
        }
        printf("%d\n",dp[6][0]);
    }
    return 0;
}











你可能感兴趣的:(dp)