uva 607 - Scheduling Lectures

Scheduling Lectures 

You are teaching a course and must cover n ( $1 \le n \le 1000$) topics. The length of each lecture is L ($1 \le L \le 500$) minutes. The topics require $t_1, t_2, \dots, t_n$ ( $1 \le t_i \le L$) minutes each. For each topic, you must decide in which lecture it should be covered. There are two scheduling restrictions:

1.
Each topic must be covered in a single lecture. It cannot be divided into two lectures. This reduces discontinuity between lectures.
2.
Topic  i must be covered before topic  i + 1 for all  $1 \le i < n$. Otherwise, students may not have the prerequisites to understand topic  i + 1.

With the above restrictions, it is sometimes necessary to have free time at the end of a lecture. If the amount of free time is at most 10 minutes, the students will be happy to leave early. However, if the amount of free time is more, they would feel that their tuition fees are wasted. Therefore, we will model the dissatisfaction index (DI) of a lecture by the formula: 

\begin{displaymath}DI = \cases{0 & if $t=0$, \cr-C & if $1 \le t \le 10$, \cr(t-10)^2 & otherwise}\end{displaymath}

where  C  is a positive integer, and  t  is the amount of free time at the end of a lecture. The total dissatisfaction index is the sum of the DI for each lecture.


For this problem, you must find the minimum number of lectures that is needed to satisfy the above constraints. If there are multiple lecture schedules with the minimum number of lectures, also minimize the total dissatisfaction index.

Input 

The input consists of a number of cases. The first line of each case contains the integer  n , or 0 if there are no more cases. The next line contains the integers  L  and  C . These are followed by  n  integers $t_1, t_2, \dots, t_n$ .

Output 

For each case, print the case number, the minimum number of lectures used, and the total dissatisfaction index for the corresponding lecture schedule on three separate lines. Output a blank line between cases.

Sample Input 

6
30 15
10
10
10
10
10
10
10
120 10
80
80
10
50
30
20
40
30
120
100
0

Sample Output 

Case 1:
Minimum number of lectures: 2
Total dissatisfaction index: 0

Case 2:
Minimum number of lectures: 6
Total dissatisfaction index: 2700

这道题最简单的想法是先贪心求解最小的lecture数,然后dp[i][j]表示前i个lecture,讲了j个topic的最小代价。

转移方程:dp[i][j]={dp[i-1][k-1],val[k][j]},这里要枚举k,但是k...j的总时长要小于等于1个lecture的长度,这个复杂度是多少呢?貌似不好估算,因为不能简单的看成3次方。

设想一下最小的lecture数越小,则说明每个lecture长度就相对较长,因此k的枚举范围增大,而如果最小的lecture数越大,则说明每个lecture长度就相对较短,因此k的枚举范围减小,可见i和k的上限是相互制约的,这样复杂度就不太好估算了,取决于lecture的长度和topic的分布情况了。

但是网上有好多用这些方法做的,应该是可以AC的,可能是后台数据较弱的原因,也可能是i,k的相互制约,使复杂度近似O(n^2),这个一时还下不了结论。

为保险起见,这题是有纯O(n^2)的解法的。

我们仍然换个视角,对最小的lecture数进行dp,而最小代价在这个过程中顺带更新吧。

不过要首先预处理一下,每个lecture中,讲i,i+1,……j-1,j这些话题的最少代价,用dp[i][j]表示。其中dp[i][j]==inf则说明[i,j]这些topic总时长超过1个lecture的长度了。

然后开两个数组,num[i]表示已经讲完前i个topic所用的lecture数,ans[i]表示已经讲完前i个topic所花的最小代价。首先得初始化为inf,表示这些状态没有更新。

首先初始化num[i]=1的情况,即第1个lecture所能覆盖到的topic情况,顺带更新ans[i]。

之后借助这些已更新的状态,去更新其他状态。有点类似刷表法。

if(num[i]+1<num[j])说明1...i+(i+1...j)这种情况会使得前j个topic所花的lecture数更少,其中i+1...j都是1个lecture来讲,当然可以肯定这时num[j]一定等于inf,因为递推顺序是不停地加1个lecture,这时ans[j]也一定是inf,直接赋值ans[i]+dp[i+1][j]即可。

当然num[j]也可以不为inf,即已被更新过,这时num[i]+1只可能等于num[j],这个时候就要取个最小的ans[j]了,即ans[j]=min(ans[j],ans[i]+dp[i+1][j]),但什么情况会出现num[i]+1=num[j]呢?很显然,比如num[i]=1时,ans[1...4]均不为inf,这表示第1个lecture最多可覆盖4个topic,那么如果第2个lecture可最多覆盖3个lecture,这时ans[5]就会在ans[2]+3,ans[3]+2,ans[4]+1(ans[2]+3表示第1个lecture讲了2个topic,并且第2个lecture讲了3个topic,其他同理)里选出最小值,等号产生于这里。


代码:

#include<cstdio>
#include<iostream>
#define Maxn 1010
using namespace std;

int a[Maxn],dp[Maxn][Maxn],num[Maxn],ans[Maxn];
int n,l,c;
const int inf=1<<30;
int getv(int x){
    if(x==0) return 0;
    if(x>=1&&x<=10) return -c;
    return (x-10)*(x-10);
}
int main()
{
    int cas=1;
    while(scanf("%d",&n),n){
        if(cas!=1) puts("");
        scanf("%d%d",&l,&c);
        for(int i=1;i<=n;i++)
            scanf("%d",a+i);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            dp[i][j]=inf;
        for(int i=1;i<=n;i++){
            int sum=0;
            for(int j=i;j<=n;j++){
                sum+=a[j];
                if(sum>l) break;
                dp[i][j]=getv(l-sum);
            }
        }
        for(int i=1;i<=n;i++) num[i]=ans[i]=inf;
        for(int i=1;i<=n;i++)
            if(dp[1][i]!=inf){
                num[i]=1;
                ans[i]=dp[1][i];
            }
        for(int i=1;i<=n;i++)
            if(num[i]!=inf){
                for(int j=i+1;j<=n;j++){
                    if(dp[i+1][j]==inf) break;
                    if(num[i]+1<num[j]){
                        num[j]=num[i]+1;
                        ans[j]=ans[i]+dp[i+1][j];
                    }
                    else if(num[i]+1==num[j])
                        ans[j]=min(ans[j],ans[i]+dp[i+1][j]);
                }
            }
        printf("Case %d:\n",cas++);
        printf("Minimum number of lectures: %d\n",num[n]);
        printf("Total dissatisfaction index: %d\n",ans[n]);
    }
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值