Bob will participate in a programming contest. There are altogether n problems in the contest. Unlike in PAT (Programming Ability Test), in a programming contest one can not obtain partial scores. For problem i, Bob will need time[i] to solve it and obtains the corresponding score[i], or he may choose not to solve it at all. Bob will be happy when he obtains a total score no less than happy_score. You are supposed to find the minimum time needed for Bob to be happy. The function need_time must return the minimum time, or -1 if it is impossible for Bob to obtain a score no less than happy_score.
Format of function:
int need_time(const int time[], const int score[], int happy_score, int n);
Here n (1≤n≤ MAXN) is the number of problems; happy_score (1≤ happy_score ≤ MAXS) is the minimum score for Bob to be happy; time[] is the array to store time[i] (1≤time[i]≤100) which is the time to solve problem i; score[] is the array to store score[i] (1≤score[i]≤100) which is the score Bob gets for solving problem i.
Sample program of judge:
#include <stdio.h>
#define MAXN 10
#define MAXS 1000
int need_time(const int time[], const int score[], int happy_score, int n);
int main() {
int n, i, happy_score;
int time[MAXN], score[MAXN];
scanf("%d %d", &n, &happy_score);
for (i = 0; i < n; ++i)
scanf("%d", &time[i]);
for (i = 0; i < n; ++i)
scanf("%d", &score[i]);
printf("%d\n", need_time(time, score, happy_score, n));
return 0;
}
/* Your function will be put here */
Sample Input:
6 121
84 87 78 16 94 38
87 93 50 22 63 28
Sample Output:
125
思路:
题目初看蛮复杂的,想清楚了会发现其实是一道典型的0/1背包问题,可以采用动态规划。
给定了一系列题目,要我们选其中的某些题目做,达到开心值,并花的时间最少。也就是题目要我们从一堆题目里选要做的,这些任务要满足:
1.开心值总和 >= happy_score
2.所花时间总和最少
我们不要从正面考虑问题,换个角度,逆向思维。我们从这一堆题目里选不做的,这些题目要满足:
1.开心值之和<=happy_score
2.所花的时间总和最多
因为一旦不做的题目达到上述两个条件了,剩下的要做的题目自然也就满足要求了。这点想清楚了就会发现题目很简单,而且就是一个0/1背包模型。
happy_score就是背包的空间,题目的开心值就是放入背包的物品的体积,而题目的耗时就是每个物品的价值。最后的耗时就是不做的题目的最大耗时,再总时间作个差就得到要做的题目的最少耗时。
源码:
int max(int a,int b)
{
return a>b?a:b;
}
int need_time(const int time[], const int score[], int happy_score, int n)
{
int i;
int sum=0;
int sum_time=0;
int min_score=MAXS;
for(i=0;i<n;i++)
{
sum+=score[i];
sum_time+=time[i];
min_score=score[i]<min_score?score[i]:min_score;
}
if(sum<happy_score)return -1;//所有题目都做也不能达到开心值
int dp[sum-happy_score+1][n+1];//dp[x][y]:在剩余x空间的背包放入前y件物品所能获得的最大价值
for(int i=0;i<=sum-happy_score;i++)
for(int j=0;j<=n;j++)
dp[i][j]=0;
for(int j=min_score;j<=sum-happy_score;j++)
{
for(int i=1;i<=n;i++)
{
if(j>=score[i-1])
//剩余背包空间足够放下第i件物品,则在放与不放中选择价值最大的情况
dp[j][i]=max(dp[j][i-1],time[i-1]+dp[j-score[i-1]][i-1]);
else
//剩余背包空间不足放下第i件物品
dp[j][i]=dp[j][i-1];
}
}
if(sum_time==dp[sum-happy_score][n])
return -1;
else
return sum_time-dp[sum-happy_score][n];
}
下面是用滚动数组优化过的版本:
int max(int a,int b)
{
return a>b?a:b;
}
int need_time(const int time[], const int score[], int happy_score, int n)
{
int i;
int sum=0;
int sum_time=0;
int min_score=MAXS;
for(i=0;i<n;i++)
{
sum+=score[i];
sum_time+=time[i];
min_score=score[i]<min_score?score[i]:min_score;
}
if(sum<happy_score)return -1;
int dp[sum-happy_score+1];
for(int i=0;i<=sum-happy_score;i++)
{
dp[i]=0;
}
int v=sum-happy_score;
for(int i=1;i<=n;i++)
{
for(int k=v;k>=score[i-1];k--)
{
dp[k]=max(dp[k],dp[k-score[i-1]]+time[i-1]);
}
}
if(sum_time==dp[v])
return -1;
else
return sum_time-dp[v];
}