POJ - 3265/USACO - Jan07 Gold Problem Solving 动态规划(DP) | 对USACO官方题解0ms程序的解释

来源:互联网 发布:linux-mint 搜狗输入法 编辑:程序博客网 时间:2024/05/18 22:46

【问题描述】
过去的日子里,农夫John的牛没有任何题目。可是现在他们有题目,有很多的题目。 精确地说,他们有 P 道题目要做。并且他们还离开了农场并且象普通人一样找到了工作,他们的月薪是M 元(上个月的工资在下月初发放)。

他们的题目是一流的难题,所以他们得找帮手。帮手们不是免费的,但是他们能保证在一个月内作出任何题目。每做一道题需要两笔付款:第一笔A_i(1 <= A_i <= M) 元在做题的那一个月初支付,第二笔B_i元(1 <= B_i <= M)在做完后的下一个月初支付。 每一个月牛们用上一个月挣的钱来付款。牛没有任何存款意识,所以每个月的节余都会拿去买糖吃掉。

因为题目是相互关连的,它们必须按顺序解出。 比如,题目3必须在解题目4之前或同一个月解出。

找出牛们做完所有题目并支付完所有款项的最短月数。

【输入格式】
第一行: M 和 P;
第2…P+1行: 第i行包含A_i和B_i, 分别是做第i道题的欲先付款和完成付款.

【输出格式】
第一行: 牛们做完题目并付完帐目的最少月数.

【输入样例】

100 540 2060 2030 5030 5040 40

【输出样例】

6

【样例解释】

【数据范围】
1 <= P <= 300
1 <= M <= 1000

第一种方法:(详见Cpp环境【Usaco2007 Jan】【CQYZOJ1432】解题)

状态函数f[i][j]=解答前i个problem且最后一次连续进行j个problem的解答所需要的最小月数。

第二种方法:(此法时间及空间复杂度更低)
代码参考USACO官方题解,注释为个人理解。(如有错误,敬请指出。)

#include<cstdio>#define inf 0X7FFFFFFFusing namespace std;int m,p,m1[305],m2[305];int dp[305],cp[305];//dp[i]:前i道题的最少月数//cp[j]:前j道题的月数最少时,最后一个月支付的钱int main(){    scanf("%d%d",&m,&p);    for(int i=1;i<=p;i++)    {        scanf("%d%d",&m1[i],&m2[i]);    }    dp[0]=1;    for(int i=1;i<=p;i++)    {        dp[i]=inf;        int x=m1[i],y=m2[i];        for(int j=i-1;j>=0 && x<=m && y<=m;j--)        {            int z=dp[j]+2-(cp[j]<=m-x); //若付完第i题剩余的钱(m-x)可以支付记录的做完前j题的最后一个月月末付款的最小值(cp[j]),则只需要1个月,否则2个月。            if(z<dp[i] || (z==dp[i] && cp[i]>y)) //有大于一种情况时,记录最后一个月月末付款的最小值            {                dp[i]=z,cp[i]=y;            }            x+=m1[j];            y+=m2[j];        }    } //个人理解为j是一个分界点;j前面的在上个月或之前完成,j到i的题在本月完成。若有多个dp[j]最小,则找本月需付的上个月的钱的最小值。    printf("%d\n",dp[p]+1);    return 0;}
0 0
原创粉丝点击