垦田计划
刷题最有用的一集!做这道题的时候,我深刻感受到之前刷的题是有效果的QWQ。如果是以前,优先队列我是想都不会想的,如果是中间,优先队列只得70我就放弃了,但是现在,我刷过蓝桥杯 砍竹子,也就对这道题的优化有了眉目。
本题可以学到的:自定义类型优先队列的使用、map实现横向"砍竹子"
使用优先队列还超时了,这是我没想到的,但是好歹得了70分,而且复习了优先队列,还是不亏的。经过我的优化,还是把这道题做出来了。
70分:
cpp
#include <iostream>
#include <queue>
using namespace std;
#define ll long long
const int N=1e5+2;
//思路:贪心吧,先将最高的那个打下来,放回去,然后再找最高,再打,直到资源用完或者最高的那个无法再减。
//看的这类问题就想起了 优先队列
struct Infor //田地信息
{
int time,cost; //时间,减少一天的花费
//使用自定义类型的优先队列需要重载运算符,多写几次就记住了
Infor(int t,int c)
{
time=t; cost=c;
}
bool operator < (const Infor& a) const //大顶堆
{
return time<a.time;
}
};
priority_queue<Infor>pq; // <当前开垦天数,减少一天的花费>
int n,m,k; //区域总数、资源总量、每块最少开垦天数
int main()
{
cin>>n>>m>>k;
int time,cost;
for(int i=0;i<n;i++)
{
cin>>time>>cost;
pq.push({time,cost});
}
while(m) //当资源没用完
{
time=pq.top().time;
cost=pq.top().cost;
if(time==k || m-cost<0) //如果当前最大天数已经无法再减少
break;
//到这都是能减少的
m-= cost;
time--;
pq.pop(); //删除旧的
pq.push({time,cost}); //添加新的
}
cout<<pq.top().time;
return 0;
}
**优化的思绪(怎么想到这样优化的):**仔细想想还有哪里可以优化呢?其实break的条件可以再优化一下,想想,如果有100个田,有99块都能被材料减少,但是就是有一个无法满足,导致这些田都白减了,会浪费很多时间。如果可以有一个更高效的判断,或者不一个一个来减,直接减一截,那是不是好很多。
对!一次性减一截!这里我想起来之前刷蓝桥杯那个砍竹子,我们不一定要将一个田减一截,而可以将相同天数的田全部都减1!前者是纵向砍一大段,后者是横向砍一片,但是这一片都只向下减少了1。
经过我漫长的优化......(我一开始用map还没做出来,因为将totalcost的计算和向下迭代复杂化了,下面的代码就很满意)
AC:
cpp
#include<iostream>
#include <map>
#include <vector>
using namespace std;
#define ll long long
const int N=1e5+2;
const int inf=0x3f3f3f3f;
int n,m,k;
map<int,vector<int>>mp; // <天数,相同天数的id集合>
vector<int>c(N);
int ans=inf;
int main()
{
cin>>n>>m>>k;
int time,cost;
for(int i=0;i<n;i++)
{
cin>>time>>c[i];
mp[time].push_back(i);
}
ll totalCost=0;
//mp也是有顺序的,我们可以反向遍历将最大的key(也就是这里的time)进行处理
auto it=mp.end(); //先指向最大的时间
it--;
time=it->first;
vector<int>cur=it->second;
while(m>0)
{
for(auto id:cur) //先将cur指向的田地加入到Totalcost
totalCost+=c[id]; //因为每次其实都是在上次的基础上进行负重增加,所以没必要从头到尾计算
if(m<totalCost || time==k)
{
cout<<time;
return 0;
}
m-=totalCost;
//逐渐向低天靠拢
time--;
cur=mp[time];
}
return 0;
}