UCF Local Programming Contest Round 1A—F题题解(带权活动选择dp)
题目链接:Trading Cards
题意简介:第一行有两个输入,t,p分别表示有t场考试,p个学生。
接下来的t行输入,每行输入一个考试的三个属性,s,e,a,分别表示考试在s的时候开始,e的时候结束,这门考试满分a分。
再接下来有p组输入,每组输入有t行,第i组第j行表示第i个学生在第j个考试上能取得分数占总分数的百分比。
最终需要输出成绩最好的三名学生的序号和成绩
题目给的数据范围是
(1 ≤ t ≤ 10000)
(3 ≤ p ≤ 100)
(0 ≤ s < e ≤ 10000)
(1 ≤ a ≤ 100)
题目读完发现对于每个学生,他们的成绩都互不干扰,所以我们只需要讨论对一个学生,如何算出他能获得的最高的分数就可以了。
此时我第一反应便是一个类背包问题,对每场考试我能有选或者不选两种可能。于是我便建立了一个二维数组dp[ i ][ j ],其中,i表示第i个考试是否选择,j表示一个时间轴。
dp[ i ][ j ] = max(dp[ i - 1 ][ j ] ,dp[ i ][ j - chengji[ j ] ])
列完容易发现要先将所有考试按照结束时间排序。
此时对该算法进行一次复杂度分析,按照背包的思路,要完整遍历二维数组,其复杂度是1e8,但是学生的数量是1e2所以显然会超时。
这是我们需要对我们的算法进行简化,简化后就是该题涉及到的知识点:带权活动选择问题
在对所有考试按照结束时间排序之后,对于每场考试,我需要判断两种情况
①不选择该场考试,此时在我这场考试的结束时间时,我能获得的最大分数就是上场考试的分数。即dp【i】=dp【i-1】
②选择该场考试,那么这场考试结束时的分数便是 这场考试获得的分数+这场考试开始前最晚结束的考试结束时的分数
在②中,由于我们已经对所有考试进行了按照结束时间的排序,所以由于单调性,我们可以在1到i-1区间用二分查找这场考试开始前最晚结束的考试。
所以我们现在可以写出新的递推:dp【i】=max(dp【i-1】,dp【erfen(i-1,test【i】.s)】)
计算复杂度时有一个n变成了logn级别,所以变成了约1e7的复杂度,可过。
除此之外,因为起始考试顺序不定,而后面给每个学生都是按照原来考试得顺序,所以要额外记录该考试的原顺序。
综上所述可写下如下代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,p;
double maxn;
double stu[105][10005];
double chengji[105][10005];
struct nodep{
double chengji;
int i;
bool operator < (struct nodep p){
return chengji<p.chengji;
}
}jidian[105];
struct node{
double s,e,a;
int i;
bool operator < (struct node p){
return e<p.e|| (e==p.e&&s<p.s);
}
}test[10005];
int erfen(int r,int start){
int l=1;
while(l<=r){
int mid=(l+r)>>1;
if(test[mid].e<=start){
l=mid+1;
}
else{
r=mid-1;
}
}
return r;
}
signed main(){
cin>>t>>p;
cout<<fixed<<setprecision(2);
for(int i=1;i<=t;i++){
test[i].i=i;
cin>>test[i].s>>test[i].e>>test[i].a;
}
for(int i=1;i<=p;i++){
for(int j=1;j<=t;j++){
scanf("%lf",&stu[i][j]);
stu[i][j]*=test[j].a;
}
}
sort(test+1,test+1+t);
for(int i=1;i<=p;i++){
maxn=0;
for(int j=1;j<=t;j++){
chengji[i][test[j].i]=max(chengji[i][test[j-1].i],chengji[i][test[erfen(j-1,test[j].s)].i]+stu[i][test[j].i]);
maxn=max(maxn,chengji[i][test[j].i]);
}
jidian[i].chengji=maxn;
jidian[i].i=i;
}
sort(jidian+1,jidian+1+p);
for(int i=1;i<=3;i++){
cout<<jidian[p+1-i].i<<" "<<jidian[p+1-i].chengji<<endl;
}
}