Chinaunix首页 | 论坛 | 博客
  • 博客访问: 112551
  • 博文数量: 23
  • 博客积分: 975
  • 博客等级: 准尉
  • 技术积分: 262
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-19 00:54
文章分类
文章存档

2011年(2)

2010年(3)

2008年(18)

我的朋友

分类:

2008-07-19 02:36:20

1.题目(原题)以及相应的解释
Problem Solving

Time Limit: 2000MS              Memory Limit: 65536K

 
Description
 

In easier times, Farmer John's cows had no problems. These days, though, they have problems, lots of problems; they have P (1 ≤ P ≤ 300) problems, to be exact. They have quit providing milk and have taken regular jobs like all other good citizens. In fact, on a normal month they make M (1 ≤ M ≤ 1000) money.

 

Their problems, however, are so complex they must hire consultants to solve them. Consultants are not free, but they are competent: consultants can solve any problem in a single month. Each consultant demands two payments: one in advance (1 ≤ payment ≤ M) to be paid at the start of the month problem-solving is commenced and one more payment at the start of the month after the problem is solved (1 ≤ payment ≤ M). Thus, each month the cows can spend the money earned during the previous month to pay for consultants. Cows are spendthrifts: they can never save any money from month-to-month; money not used is wasted on cow candy.

 

Since the problems to be solved depend on each other, they must be solved mostly in order. For example, problem 3 must be solved before problem 4 or during the same month as problem 4.

 

Determine the number of months it takes to solve all of the cows' problems and pay for the solutions.

 
2.算法思想说明

该题目采取动态规划(dynamic programming)解决。

最优解结构——problem
从题目得知,有p个problem等待解决,而且,这些problem是有顺序的,那么,可以想到,每个problem的“预付款”和“后付款”串连起来,形成一个problem链。整一条problem链可以分解成两条problem链,当这两条problem链的组合为子问题的最优解时,合并成的problem链也是最优解。
寻找最优解的目标是总耗时(月数)最小。
Problem链的合并存在三种可能性,如下图:

最优解的选择取决于两点:1、合并的月份不能超过每月所能赚的钱;2、耗时尽量取短的。
合并只存在着三种情况,对于每一条problem链,只须记录前两个月的花费和最后两个月的花费即可,中间月份的花费在后续的合并中不会受到影响。因此,对于每一条problem链记录采用以下数据结构:
struct situation
{

       int fb,fa,lb,la,months;

}
fb、fa、lb、la分别为前两月和后两月的花费,months为该问题链的耗时。
递归关系

pst[i][j]=min{pst[i][k] merge pst[k+1][j]} (i<=k<=j)

      =(bi ai months:2) (i=j)
其中merge为上述的“合并”,在程序中,由merge()函数实现。
计算最优解


计算次序如上图所示,伪代码如下:
for r=0 to p-1

   for i=0 to p-r-1

      pst[i][i] merge p[i+1][i+r]

       for k=i+1 to i+r

        if pst[i][k] merge p[k+1][i+r] cost months less than current one

            replace it

最终解为(pst[0][p-1].months + 1),因为第一个月还没赚到钱,要从第二个月开始解决问题,所以要加1。
 
3.代码+注释
#include
#include
 
struct situation
{

       int fb,fa,lb,la,months;

       //fb and fa are the first two payments of the problem chain

       //lb and la are the last two payments of the problem chain

       //months is the cost of the problem chain

} pst[300][300];//the DP table
 
int m;//the amount of money earned per month
 
int p;//the amount of problems
 
//merge i~k and (k+1)~(i+r)
int* merge(int i,int r,int k,int result[5])
{
       result[0]=pst[i][k].fb;
       result[1]=pst[i][k].fa;
       result[2]=pst[k+1][i+r].lb;
       result[3]=pst[k+1][i+r].la;
 

       //if they can be merged overlaying 2 month

       if(pst[i][k].lb+pst[k+1][i+r].fb<=m

              && pst[i][k].la+pst[k+1][i+r].fa<=m)

       {
              result[4]=pst[i][k].months+pst[k+1][i+r].months-2;

              //to amend

              if(pst[i][k].months==2)
              {
                     result[0]+=pst[k+1][i+r].fb;
                     result[1]+=pst[k+1][i+r].fa;
              }
              if(pst[i][k].months==3)
                     result[1]+=pst[k+1][i+r].fb;
              if(pst[k+1][i+r].months==2)
              {
                     result[2]+=pst[i][k].lb;
                     result[3]+=pst[i][k].la;
              }
              if(pst[k+1][i+r].months==3)
                     result[2]+=pst[i][k].la;
       }

       //if they can be merged overlaying 1 month

       else if(pst[i][k].la+pst[k+1][i+r].fb<=m)

       {
              result[4]=pst[i][k].months+pst[k+1][i+r].months-1;

              //to amend

              if(pst[i][k].months==2)
                     result[1]+=pst[k+1][i+r].fb;
              if(pst[k+1][i+r].months==2)
                     result[2]+=pst[i][k].la;
       }

       //they can be merged overlaying 0 month

       else
              result[4]=pst[i][k].months+pst[k+1][i+r].months;
 

       return result;

}
 
int solve()
{

       int r,i,k,temp[5];

       //r is the len of problems

       //i is the first problem

       //k is the break point

       //thus,merge i~k and (k+1)~(i+r)

       //temp use to receive the return of merge()

       //temp[0] is b,temp[1] is a,temp[2] is months

 
       for(r=1;r
              for(i=0;i
              {

                     //write the case that k=i in the table

                     merge(i,r,i,temp);
                     pst[i][i+r].fb=temp[0];
                     pst[i][i+r].fa=temp[1];
                     pst[i][i+r].lb=temp[2];
                     pst[i][i+r].la=temp[3];
                     pst[i][i+r].months=temp[4];
                     for(k=i+1;k
                     {
                            merge(i,r,k,temp);

                            //when the months in this case less than the current months,replace it

                            if(temp[4]
                            {
                                   pst[i][i+r].fb=temp[0];
                                   pst[i][i+r].fa=temp[1];
                                   pst[i][i+r].lb=temp[2];
                                   pst[i][i+r].la=temp[3];
                                   pst[i][i+r].months=temp[4];
                            }
                     }
              }
 

              return pst[0][p-1].months+1;

              //because the first month has no money, we have to +1

 
}
 
int main()
{

       int i;

 

       scanf("%d %d",&m,&p);

       for(i=0;i
       {

              scanf("%d %d",&pst[i][i].fb,&pst[i][i].fa);

              pst[i][i].lb=pst[i][i].fb;
              pst[i][i].la=pst[i][i].fa;
              pst[i][i].months=2;
       }
       printf("%d",solve());
 

       return 0;

}
 
4.测试数据

Sample Input

 
100 5
40 20
60 20
30 50
30 50
40 40
 

Sample Output

 
6

Sample Input

 
700 12
177 138

1   86

201 219
202 198
214 124
150 90
56 139
43 28
72 137
70 105
16 15
220 167
 

Sample Output

 
6
 
5.算法效率分析
下图为ACM网上题库提交的结果:

该算法耗时主要在于solve()函数的三重循环,因此,该算法的时间复杂度为O(n3)。
如果采用分治法,每一个问题有3种可能性,分别是和前一问题同时解决,在前一问题后一个月解决,在前一问题后两个月解决。那么该算法的时间复杂度为O(3n),可能会无法通过时间限制。
在分治法递归的过程中,存在很多重复的运算。因此,该题选用动态规划算法更为高效。
阅读(941) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~