任务
我觉得这道题目如果只有第一个问题的话,排序方式是多种多样的,而且考虑的对象也可以是机器
比如我可以给机器按照\(y\)从小到大排序,然后依次考虑每个机器,对于每个机器,在能选择的任务中选择\(x\)最大的即可
但这个时候就没有办法保证价值最大了,所以这道题启发我们,如果一道题目有多维的因素需要考量(这题目既要考虑任务个数又要考虑最终价值),对于多个对称的对象(比如这题的任务与机器,\(x\)和\(y\)),我们要从每一个身上出发,去寻找符合题意的考虑对象
而且对于数字也要敏感,这道题目的两个加权数相差如此之大,我们要有一个感性的理解,要优先考虑\(x\),再对\(y\)排序(证明见下)
而且为了价值最大,我们要以任务为考虑目标,这样就能保证能选的任务尽量被选择
此时,我们用数学归纳法来证明一下:假设已经选择的任务是最优方案的一部分,对于最新考虑的任务,如果他能够被完成,那么是一定会被完成的,问题就在于他要选择哪一个机器完成,显然是选择\(y\)值最小的完成,因为此时按照\(x\)递减排序,\(x\)已经不再是一个考虑因素。那么按照这种方案选择下来,看起来方案是唯一的,为什么就刚好能够保证价值最大呢?其实,对于最新考虑的任务,他之所以能被完成就一定被完成,是因为这样才保证第一个条件满足,然而他不被完成第一个条件一定不能够被满足吗?当然也不是,所以这就是这个方案的不唯一性。然而,我就算不完成这个任务,而去完成后面的任务导致第一个条件被满足了,但由于\(y\)的差值最大是\(100\),乘以\(2\)也就是\(200\),远小于\(500\),所以这么选就刚好保证了价值最大(当然此时也有可能\(x\)不变,但是我们对\(x\)不变的任务是按照\(y\)从大到小排序的,此时\(y\)一定会减少)
所以我们可以直接考虑我们已经选择的任务是满足两个条件的最优方案的一部分(前面都只考虑了满足第一个条件),然后进行证明
如果我们以机器为考虑对象,那对于一个机器就应该会在其能够完成的任务中尽量选择\(y\)最大的(此时没有考虑\(x\)),就有可能不能够让价值最大了
而且此时甚至连第一个条件都无法保证。实际上,如果从小到大排序就必须以机器为考虑对象,从大到小排序就必须以任务为考虑对象,这样才可以满足依次考虑后才可以满足第一个条件
update 2025.1.8
也可以以机器为考虑的对象,做法如下:将机器按照\(y\)递增排序,扫描机器,对于当前的机器,找出所有\(y\)不大于当前机器\(y\)的任务,再在里面找出\(x\)最大且不大于当前机器\(x\)的任务给当前机器做就可以了
这个写法要对multiset
重载小于运算符,可见如下代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m;
struct Node
{
ll x,y;
bool operator<(const Node &a) const
{
if(a.x==x) return a.y<y;
return a.x<x;
}
//这里的排序规则是,按照x递减排序,如果x相同就按照y递减排序
}mac[100010],ta[100010];
ll ans;
int cnt;
multiset<Node> s;
set<Node>::iterator it;
bool cmp(Node i,Node j)
{
return i.y<j.y;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&mac[i].x,&mac[i].y);
}
sort(mac+1,mac+n+1,cmp);
for(int i=1;i<=m;i++)
{
scanf("%lld%lld",&ta[i].x,&ta[i].y);
}
sort(ta+1,ta+m+1,cmp);
int flag=1;
for(int i=1;i<=n;i++)
{
while(ta[flag].y<=mac[i].y&&flag<=m)
{
s.insert(ta[flag]);
flag++;
}
if(!s.empty())
{
if((it=s.lower_bound(mac[i]))!=s.end())
{
ans+=500*(*it).x+2*(*it).y;
s.erase(it);
cnt++;
}
}
}
printf("%d %lld",cnt,ans);
return 0;
}