博客园 首页 私信博主 显示目录 隐藏目录 管理

洛谷 题解 P3161 【[CQOI2012]模拟工厂】

本蒟蒻又双叒叕被爆踩辣!

题目链接

Solution:

这题又是一道贪心。。

数据范围:

n<=15 ti<=100,000 gi<=10^9 mi<=10^9

这里就可以看到几个小细节,也可以想出一些思路。

要开long long!

首先,n <= 15, 这个数据可以说是很小辣,它代表有n个任务,又因为

如果接受订单i,则必须恰好在时刻ti交易,不能早也不能晚。同一时刻可以接受多个订单,但每个订单只能被接受一次。

窝们本不知道选择哪些任务才会是利润最高,而且这很明显也无法通过一些规律来知道选择哪个会最优。

所以窝们应该怎么办???

枚举!!!

因为只有数据范围只有15,所以窝们可以枚举1到(1 << n) - 1用二进制表示当前这个任务选不选,那我们已经解决了这个任务辣!

接下来就是,然后处理每个任务方案。

窝们针对每个任务方案,依次判断它的可行性。

然后在可行的方案中找出利润最大的(就是保证任务完成的情况下,保证生产力更大。

假设窝们当前有那么对于订单i和j,我们都会得到方程:make为完成订单i时的生产力,time为距离j订单的时间,time_create为用来提升生产力的时间,need是订单j需求量

(make + time_create) × (time − time_create) = need

化简就是:

time_create * time_create + (make - time) * time_create + need - make * time = 0;

只要保证起“根的判别式” >= 0就行。

所以复杂度应该是O(2 的 n次方 * n 的平方);

具体见代码,这里只有一些思路,个人介意对着代码读,更容易理解

Code:

#include<bits/stdc++.h>

using namespace std;

#define maxn 25
#define maxm
#define ll long long//mi数据1e9,你不开longlong看看 
#define int long long
#define Rep(x, a, b) for(int x = a; x <= b; ++ x)
#define Dep(x, a, b) for(int x = a; x >= b; -- x)
#define Next(x, u) for(int i = head[u]; i ; i = e[i].nxt)

//int read(){窝不喜欢用快读,但缺省源里面放了,所以也没删 
//	int f = 1, x = 0;
//	char c = getchar();
//	while(c < '0' || c > '9'){
//	if(c == '-'){
//	f = -1;
//	}
//	c = getchar();
//	}
//	while(c >= '0' && c <= '9'){
//	x = (x << 1) + (x << 3) + (c ^ 48);
//	c = getchar();
//	}
//	return x * f;
//}

struct node{
int t, g, m;//都如题,t是在什么时刻交易,g是需要多少货,m是可以得到的利润 
}e[maxn], s[maxn];

//e是记录所有的任务
//s是记录当前情况窝们已经选择的任务 

int n, ans;//不多说

bool cmp(node x, node y){
return x.t < y.t;
}//要先按照t排序,要不不好处理

int discriminant(int b, int c){//这个单词是“判别式”,百度说的
if(b * b - 4 * c < 0){//都知道二次函数根的判别式吧(b * b - 4 * a * c) 
return -1;//返回-1,即根的判别式小于0 
}
return floor((-b + sqrt(b * b - 4 * c)) / 2);//否则就返回它的根:((-b + sqrt(b * b - 4 * a * c) / 2 / a) 
}

void dfs(int x){
int now_have = 0, make_force = 1, ans_now = 0, tot = 0;
//now_have是窝们当前拥有的货物
//make_force是当前的生产力
//ans_now是这种方案的利润 
//tot是当前这种选择方案任务的个数 
Rep(i, 1, n){//这里就是寻找当前方案的任务 
if(x & (1 << i - 1)){//用二进制 
s[++ tot] = e[i];//进入s 
ans_now += e[i].m;//就是当前方案的总利润 
}
}
Rep(i, 1, tot){
int t = s[i].t - s[i - 1].t, sum = 0;//sum是所需要的货物 
//t是时间,s[i].t - s[i- 1].t是它的最大值,因为当它前面的货物足够购买这两个货物时,它的t就是此值 
Rep(j, i, tot){//算t的值 
sum += s[j].g;//累加所需要的货物 
if(now_have >= sum){//如果货物还够,那就下一层循环 
continue;
}//否则,就更新t,为t,和它的根的最小值 
t = min(t, discriminant(make_force - s[j].t + s[i - 1].t, sum - now_have - make_force * (s[j].t - s[i - 1].t)));
}
if(t < 0){//如果无法完成次方案 
return;//return 
}
make_force += t;//生产力就要加,(尽可能加就行 
now_have += make_force * (s[i].t - s[i - 1].t - t) - s[i].g;//其余就现在有的产量 
}
ans = max(ans, ans_now);//更新值 
}

signed main(){
scanf("%lld", &n);//输入 
Rep(i, 1, n){//输入每个任务 
scanf("%lld%lld%lld", &e[i].t, &e[i].g, &e[i].m);
}
sort(e + 1, e + n + 1, cmp);//按时刻排序 
Rep(i, 0, (1 << n) - 1){
dfs(i);//每种方案跑一遍 
}
printf("%lld", ans);//输出 
return 0;
}

下面还给两组数据

Inout:
5
5 5 5
7 7 7
9 9 9
11 1 15
16 18 778

Output:
814

Input:
10
1 1 1
2 2 2
3 3 3
4 4 4
5 5 5
6 6 6
7 7 7
8 8 8
9 9 9
10 10 10

Output:
30

Ps:请看懂再抄

posted @ 2019-12-12 11:37  Flash_plus  阅读(129)  评论(0编辑  收藏  举报