CSP 202303-2 垦田计划 暴力枚举/二分搜索/贪心

题目

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

解法一:枚举

枚举是很重要的基础解法,遍历每一种结果直到满足你设置的边界条件为止
在本题中,枚举从k开始由小到大的天数,判断缩短至该天时m的资源是否足够,如果能满足k就输出k并结束,不能满足就放宽要求(增加截止日期)直到资源缩短到某一天不够用为止。

另外在计算缩到某天所需的资源时,要判断这天是否比任务本身所需的时间还大,如果是就不必分配资源了(还没到要缩短天数的要求)

70分代码(线性枚举:超时):

双层嵌套循环O(n^2)会超时

#include <bits/stdc++.h>

using namespace std;

int t[100005];
int c[100005];

int main(){
	int n,m,k;
	int totalneed = 0;
	cin >> n >> m >> k;
	for(int i = 0;i < n;i++){
		cin >> t[i] >> c[i];
		totalneed += (t[i]-k)*c[i];
	} 
	//资源足够缩到k天 
	if(m >= totalneed){
		cout << k;
		return 0;
	}
	int res = k;
	//从小到大枚举天数,计算res天需要的资源,满足就结束 
	do{
		res++; 
		totalneed = 0;
		for(int i = 0;i < n;i++){
			//判断是否需要分配资源缩短到res天
			if(t[i] >= res){
				totalneed += (t[i]-res)*c[i];
			} 
		}
	}while(m <= totalneed); 
	cout << res;
	return 0;
}

100分代码(二分搜索/二分枚举):

在这里插入图片描述

由于本题的答案处于从k天起到第 max(t[i]) 天,如下图:
在这里插入图片描述
具备线性单调的特点,采取二分枚举在 [k,max(t[i])] 内的天数,总会找到一个符合条件的解。

#include <bits/stdc++.h>

using namespace std;

int t[100005];
int c[100005];

int n,m,k;

bool canReduce(int target){
	int totalneed = 0;
	for(int i = 0;i < n;i++){
		//判断是否需要分配资源缩短到res天
		if(t[i] >= target){
			totalneed += (t[i]-target)*c[i];
		} 
	}
	if(totalneed > m){//资源不够 
		return false;
	}
	return true;
}

int main(){
	
	cin >> n >> m >> k;
	int maxt = -1;
	
	for(int i = 0;i < n;i++){
		cin >> t[i] >> c[i];
		if(maxt < t[i]){
			maxt = t[i];
		}
	} 
	
	//二分搜索找左边界(最小值)
	int left = k;
	int right = maxt;
	
	while(left <= right){
		int mid = (left + right)/2;
		//能缩短到mid天就继续尝试缩短 
		if(canReduce(mid)){
			right = mid-1; 
		}
		//不能缩短到mid天就放宽要求mid+1 
		else{
			left = mid+1;
		}
	} 
	cout << left;
	return 0;
}

需要特别注意while内的判断条件是否取等号!
不取等号会忽略掉区间内最后一个数的判断,也就是[left,left] (此时left == right),从而取不到left的值。

解法二:贪心

在这里插入图片描述
题目已经说明,总耗时取决于用时最长的区域,所以每次分配资源给最长的任务,使其时间缩短就可以达到目的。

90分代码(超时):

存在超时的问题,原因是每次计算缩短所需资源后又用了一个for将能缩短的任务缩短,实际上,sort之后最长的天数就是task.t[0],每次将与 task.t[0] 相同的任务缩短一天后,接着上一次的位置继续判断即可,不需要再用一个for将这些任务挨个减少1天,详见 下面的代码

#include <bits/stdc++.h>

using namespace std;

struct task{
	int t;
	int c;
};

task tasks[100005];

bool cmp(const task &a,const task &b){
	return a.t > b.t;
}
int main(){
	int n,m,k;
	cin >> n >> m >> k;
	for(int i = 0;i < n;i++){
		cin >> tasks[i].t >> tasks[i].c;
	}
	sort(tasks,tasks+n,cmp);
	
	while(tasks[0].t != k){
		long long needc = 0;
		int end = 0; //用于确定相同耗时的末尾索引  
		//计算最大耗时缩短一天需要的资源 
		for(int i = 0;i < n;i++){
			//判断有几个最大(最大耗时可能有多个相同的) 
			if(tasks[0].t != tasks[i].t){
				break;
			}
			else if(tasks[0].t == tasks[i].t){
				needc += tasks[i].c;
				if(i != 0){	//排除第一个
					end++;
				}
			}
		} 
		
		if(m >= needc){
			m -= needc;
			//这里不需要(将耗时相同的任务挨个减少一天)
			for(int i = 0;i <= end;i++){
				tasks[i].t--;
			} 
		}else{//资源不足退出 
			break;
		} 
	}
	cout << tasks[0].t;
	return 0;
} 

100分代码:

实际上, task.t[0] 代表了最长的耗时(输出时也是输出task.t[0]),所以每次计算所需资源后将比较时长的 起始位置 设置为 缩短后的第一个任务tasks[start].t

#include <bits/stdc++.h>

using namespace std;

struct task{
	int t;
	int c;
};

task tasks[100005];

bool cmp(const task &a,const task &b){
	return a.t > b.t;
}
int main(){
	int n,m,k;
	cin >> n >> m >> k;
	for(int i = 0;i < n;i++){
		cin >> tasks[i].t >> tasks[i].c;
	}
	sort(tasks,tasks+n,cmp);
	
	long long needc = 0;
	int start = 0; //用于确定每次开始比较时的第一个任务
	while(tasks[0].t != k){
	
		//计算最长耗时的个数,并计算这些任务缩短一天所需的资源 
		if(tasks[0].t == tasks[start].t){
			needc += tasks[start].c;
			start++;
		} 
		else if(tasks[0].t != tasks[start].t){
			if(m >= needc){
				//资源满足就缩短一天 
				m -= needc; 
				tasks[0].t--;
			}else{//资源不足退出 
				break;
			} 
		}
		
	}
	cout << tasks[0].t;
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值