P6359 [CEOI2018] Cloud computing 题解

P6359 [CEOI2018] Cloud computing题解:

题目背景:

译自 CEOI2018 Day1 T1. Cloud Computing。

题目描述:

Johnny 创立了 Bytecomp,一家提供云计算能力的公司。这样的公司通常拥有许多快速的计算机,可以让客户在上面进行计算。

Johnny 仍然没有购买任何机器。他去了一家计算机商店,收到了一个包含所有 n n n 台可用的计算机的清单。每台计算机都可以用三个属性描述:处理器内核数 c i c_i ci,时钟频率 f i f_i fi 和价格 v i v_i vi。这样的计算机包含 c i c_i ci 个独立的内核,所以他们可以被分配不同的任务。

当客户订购资源时,她会指定所需的内核数 C j C_j Cj 和最小的时钟频率 F j F_j Fj。订单还包含客户愿意支付的价格 V j V_j Vj。如果接受订单,Bytecomp 需要提供客户所需计算能力的独占访问权。Johnny 需要选择 C j C_j Cj 个核心(可能来自不同的计算机),且它们的时钟频率至少为 F j F_j Fj,这些核心不能被分配给其它订单。

帮助 Johnny 赚尽可能多的钱:接受一个最优的订单子集,选择所有计算机的一个子集来满足所有接受了的订单。你的目标是最大化利润,即为客户提供计算能力的收入与购买计算机的成本之差。

输入格式:

标准输入的第一行包含一个整数 n n n,表示商店中可用计算机的数量。

接下来 n n n 行,每行描述一台计算机,包含三个空格隔开的整数 c i , f i , v i c_i, f_i, v_i ci,fi,vi,分别表示内核数,时钟频率和价格。

接下来一行一个整数 m m m,表示客户的订单数量。

接下来 m m m 行,每行描述一个订单,包含三个空格隔开的整数 C j , F j , V j C_j, F_j, V_j Cj,Fj,Vj,分别表示需要的核心数,最低的允许的时钟频率以及预算。

输出格式:

标准输出唯一的一行包含一个整数,表示能够得到的最大利润。

样例 :

样例输入 :

4
4 2200 700
2 1800 10
20 2550 9999
4 2000 750
3
1 1500 300
6 1900 1500
3 2400 4550

样例输出 :

350
样例解释:

一共有四台可用的计算机和三个订单。

最佳方案是购买两台价格为 700 700 700 750 750 750(总计 1450 1450 1450)的四内核的计算机,然后接受前两个订单获得 300 + 1500 = 1800 300+1500=1800 300+1500=1800 的收益。

我们获得了四个时钟频率为 2000 2000 2000 的内核,和四个时钟频率为 2200 2200 2200 的内核。可以将其中六个分配给第二个订单(需要 1900 1900 1900 的时钟频率),再将其中一个分配给第一个订单(需要 1500 1500 1500 的时钟频率),剩下一个核心不使用,这是允许的。

总利润为 1800 − 1450 = 350 1800-1450=350 18001450=350

数据规模与约定:

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 2 × 1 0 3 ,   1 ≤ c i , C i ≤ 50 ,   1 ≤ f i , F i , v i , V i ≤ 1 0 9 1\le n\le 2\times 10^3,\ 1\le c_i,C_i\le 50,\ 1\le f_i,F_i,v_i,V_i\le 10^9 1n2×103, 1ci,Ci50, 1fi,Fi,vi,Vi109

所有测试数据被划分成若干个有附加限制的子任务,每个子任务中包含若干测试点。

子任务 附加限制 分值
1 1 1 n ≤ 15 n \le 15 n15 18 18 18
2 2 2 m ≤ 15 m \le 15 m15 18 18 18
3 3 3 n , m ≤ 100 n,m \le 100 n,m100 c i = C j = 1 c_i = C_j = 1 ci=Cj=1 18 18 18
4 4 4 f i = F j = 1 f_i = F_j = 1 fi=Fj=1 18 18 18
5 5 5 v i = V j = 1 v_i = V_j = 1 vi=Vj=1 18 18 18
6 6 6 无附加限制 10 10 10

算法:贪心+动态规划:

  • 可以将买进的电脑和订单存到同一个结构体数组里面,买进的价格用负数表示。

  • 要卖出电脑,首先要买进数量 ≥ \ge 订单需求的核心数并且时钟频率 ≥ \ge 订单需求。因此,我们可以用贪心思想,把电脑按时钟频率从大到小排序,保证卖出时,之前买进的时钟频率 ≥ \ge 订单需求。

  • 排序时,若时钟频率相同,买进的电脑应排在前面,不然还没买进就要卖出了,结果显然偏小。

  • 排完序后,我们对每一台电脑(订单),都有两种选择:买(卖)或不买(不卖)。很明显是01 背包。用 d p j dp_j dpj 表示核心数为 j j j 时,收获的最大收益。

注意:

不开 long long 见祖宗~

具体实现见代码(注释比较详细~)。

代码实现:

#include
using namespace std;
using ll=long long;//不开long long见祖宗 
const ll N=2e5+10;
ll n,m,ct,ans,dp[N];
struct Pc{
	ll c,f,v;
}s[N];
bool cmp(Pc l,Pc r){
	//按时钟频率从大到小排序 
	if(l.f==r.f) return l.v<r.v;
	return l.f>r.f;
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>s[i].c>>s[i].f>>s[i].v;
		s[i].v=-s[i].v;//为了区分购进的电脑和订单,电脑的价格用负数存 
	}
	cin>>m;
	for(int i=n+1;i<=n+m;i++) cin>>s[i].c>>s[i].f>>s[i].v;
	//用一个结构体存电脑和订单 
	sort(s+1,s+n+m+1,cmp);
	memset(dp,128,sizeof(dp));
	//初始化收益为无穷小,因为购进电脑时,价格为负 
	dp[0]=0;
	for(int i=1;i<=n+m;i++){
		//01背包 
		if(s[i].v<0){
			for(int j=ct;j>=0;j--)
				dp[j+s[i].c]=max(dp[j+s[i].c],dp[j]+s[i].v);
			ct+=s[i].c;//ct记录当前的核心数,确定dp的范围 
		}else{
			for(int j=0;j<=ct-s[i].c;j++)
				dp[j]=max(dp[j],dp[j+s[i].c]+s[i].v);
		}
	}
	//ans初始化为0,因为不购进任何电脑时,收益为0 
	for(int j=0;j<=ct;j++) ans=max(ans,dp[j]);
	cout<<ans;
	return 0;
}

感谢大家的支持~

你可能感兴趣的:(题解,c++,动态规划)