这个题是一次比赛的题,是我第一次背着父母熬夜打CF。当时我开始做这个题的时候是晚上12:00,我爸突然进来告诉我叫我早点睡(没骂我已经很好了),当时心态直接崩了,明明会做,却挂了。(我现在写这篇博客是强忍着心理阴影在讲。。。)
题意简述
给定 n n n个歌曲,每个歌曲有长度值len和美丽值bea。选出最多(珂以<=) k k k个,使得这些歌曲中bea的最小值 × \times ×len的和 最大。
数据
输入:
4 3
4 7
15 1
3 6
6 8
输出:
78
输入:
5 3
12 31
112 4
100 100
13 55
55 50
输出:
10000
思路
暴力枚举肯定超时。
线段树?好像也不能做。
。。。?
。。。?
。。。?
。。。?
(吓得我蒙成一个蒟阵)
我们用
t
t
t表示这些歌曲(同代码中)。然后,按照这类问题的套路,我们需要先枚举一个
t
[
i
]
.
b
e
a
t[i].bea
t[i].bea,并且强制它是最小值。
如何强制呢?
我们按
b
e
a
bea
bea降序把
t
t
t排一下序,然后由于
b
e
a
bea
bea是降序的,我们强制
t
[
i
]
.
b
e
a
t[i].bea
t[i].bea是最小,相当于我们必须选
t
[
i
]
.
b
e
a
t[i].bea
t[i].bea,并且
t
[
i
后面
]
.
b
e
a
t[i\text{后面}].bea
t[i后面].bea都不能选。这样就简单很多了:我们枚举
t
[
i
]
.
b
e
a
t[i].bea
t[i].bea的时候,就相当于只考虑
1
1
1~
i
i
i。
然后我们过一遍,如果没选满
k
k
k个,则
i
i
i一定要选。如果选满了
k
k
k个,那我们一定是找一个
l
e
n
len
len最小的,踢掉,最划算。找最小的过程就用优先队列维护。
最后输出找到的最大即可。
代码:
#include<bits/stdc++.h>
#define int long long
#define N 1001000
using namespace std;
struct song//歌曲
{
int len,bea;
const bool operator<(const song CompWith) const
{
return bea>CompWith.bea;//按bea从大到小
}
}t[N];//和思路部分的名字同步
int n,k;
void Input()
{
scanf("%I64d%I64d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%I64d%I64d",&t[i].len,&t[i].bea);
}
}
priority_queue<int,vector<int>,greater<int> >Q;
//维护最小的len
//一定要写greater<int>,这样每次取Top就变成最小了
void Solve()
{
sort(t+1,t+n+1);//先排序
int ans=0,sum=0;//ans的初始值珂以是0,珂以是-1,也可以是-1000000000
//但sum不一样,是记录和的,一开始一定要是0
for(int i=1;i<=n;i++)
{
if (Q.size()<k)//没选满k个
{
Q.push(t[i].len);//选上
sum+=t[i].len;
ans=max(ans,sum*t[i].bea);//更新答案
}
else
{
sum-=Q.top();Q.pop();//踢掉那个最小的
Q.push(t[i].len);//选上新的
sum+=t[i].len;
ans=max(ans,sum*t[i].bea);//更新答案
}
}
printf("%I64d\n",ans);
}
main()
{
Input();
Solve();
return 0;
}