UCF Local Programming Contest Round 1A---F题题解(带权活动选择dp)

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;
	}
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值