这类算法大多都需要借助优先队列实现,用于求某种类型前 k k k 大/小的方案。
AcWing 1262. 鱼塘钓鱼
思路:使用优先队列帮助我们进行最优的选择,即每次应该去哪个地方进行钓鱼,但同时我们需要处理出中间路程所花费的时间,因为数据范围比较小,所以我们可以对所到达的最远的池塘进行遍历,然后运用优先队列进行维护即可。
同时解释一个问题,在一开始,我们便减去了路上所需要的时间,在后续即使出现了例如
a
−
b
−
c
−
b
−
a
a-b-c-b-a
a−b−c−b−a这种情况,我们也不需要进行考虑路上的消耗时间,因为我们计算的只是最终过程,即对于上述,我们需要在
a
a
a点进行两次,
b
b
b点进行两次,
c
c
c点进行一次,那么我们可以在
a
a
a点把两次鱼钓完了再去
b
b
b点,对于
b
b
b点同理。因此路上的消耗只需要考虑一遍即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<int, 3> ar;
int mod = 1e9+7;
// const int maxv = 4e6 + 5;
// #define endl "\n"
void solve()
{
int n;
cin>>n;
vector<int> a(n+5),b(n+5),s(n+5);
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=2;i<=n;i++) {
cin>>s[i];
s[i]+=s[i-1];
}
int t;
cin>>t;
int ans=0;
for(int i=1;i<=n;i++){
int rt=t-s[i];
priority_queue<pll> q;
for(int j=1;j<=i;j++){
q.push({a[j],j});
}
int res=0;
while(!q.empty()&&rt>0){
auto [ai,id]=q.top();
q.pop();
res+=ai;
rt--;
ai-=b[id];
if(ai>0) q.push({ai,id});
}
ans=max(ans,res);
}
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}
AcWing 4656. 技能升级
思路:对于较小的数据范围,我们可以通过优先队列去得到答案,即把最优结果从队首取出,然后把削减后的价值再次入队。但这题数据范围过大,正解为二分。
AcWing 1378. 谦虚数字
思路:对于这种求第k大的题目,我们可以使用优先队列,每次把队首弹出来,然让队首和序列的每个值相加后放入队列中,这样循环k次即为答案。
但是因为这题数据范围过大,具体的时间复杂度为
M
N
l
o
g
m
MNlog_m
MNlogm,因此会有一个点超时,所以我们需要使用另一种方法。
我们可以知道,对于第
k
k
k 个数,其一定是由第
k
−
1
k-1
k−1 个数乘序列中的某个的来,所以我们可以三重循环去求出第
k
k
k 个数,第一层枚举现在是求第
i
i
i 大的数,第二层循环为枚举
1
−
(
i
−
1
)
1-(i-1)
1−(i−1)中的数,第三层则是枚举原序列。由于肯定会超时,所以考虑优化,我们可以使用一个数组记录对于第二层的每一个数,其临界的合法状态,我当前的
f
[
k
]
f[k]
f[k]肯定是由
f
[
k
−
1
]
f[k-1]
f[k−1]乘上某个数转移而来,那么我就使用数组记录每个数最大合法的
f
[
i
]
f[i]
f[i],使得
a
[
j
]
×
f
[
i
]
≤
f
[
k
−
1
]
a[j]\times f[i]\leq f[k-1]
a[j]×f[i]≤f[k−1]
因此使用数组进行记录
i
i
i的值即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<int, 3> ar;
int mod = 1e9+7;
// const int maxv = 4e6 + 5;
// #define endl "\n"
void solve()
{
int n,k;
cin>>n>>k;
vector<int> a(n+5);
for(int i=1;i<=n;i++) cin>>a[i];
vector<int> ans(k+5);
vector<int> st(k+5);
ans[0]=1;
for(int i=1;i<=k;i++){
int res=2e9;
for(int j=1;j<=n;j++){
while(a[j]*ans[st[j]]<=ans[i-1]) st[j]++;
res=min(res,a[j]*ans[st[j]]);
}
ans[i]=res;
}
cout<<ans[k]<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
// cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}
暴力做法:
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<int, 3> ar;
int mod = 1e9+7;
// const int maxv = 4e6 + 5;
// #define endl "\n"
void solve()
{
int n,k;
cin>>n>>k;
k--;
vector<int> s(n);
set<ll >se;
for(int i=0;i<n;i++){
int x;
cin>>x;
s[i]=x;
se.insert(x);
}
for(int i=0;i<k;i++){
auto t=*se.begin();
se.erase(t);
for(int j=0;j<n;j++) se.insert(s[j]*t);
}
cout<<*se.begin()<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
// cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}
abc297 E - Kth Takoyaki Set
思路:和上一题一样,只不过把乘法换成了加法而已。又因为数据范围比较小,所以直接暴力跑出结果即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef pair<int,int> pll;
typedef long long ll;
void solve()
{
int n,k;
cin>>n>>k;
vector<ll> a(n);
for(int i=0;i<n;i++){
cin>>a[i];
}
set<ll> z;
for(int i=0;i<k;i++){
ll res=*z.begin();
z.erase(res);
for(int j=0;j<n;j++){
z.insert(a[j]+res);
}
}
cout<<*z.begin()<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t=1;
while(t--){
solve();
}
system("pause");
return 0;
}
AcWing 146. 序列
思路:觉得比较难的一道题目了,不知道为什么给的简单。
考虑只有两个数的情况,当我们对两个数组进行排序后,最小值一定是
a
[
1
]
+
b
[
1
]
a[1]+b[1]
a[1]+b[1];次小值一定为
m
i
n
(
a
[
1
]
+
b
[
2
]
,
a
[
2
]
+
b
[
1
]
)
min(a[1]+b[2],a[2]+b[1])
min(a[1]+b[2],a[2]+b[1]),由此推广到第
k
k
k小值,其一定为
m
i
n
(
a
[
k
]
+
b
[
k
+
1
]
,
a
[
k
+
1
]
+
b
[
k
]
)
min(a[k]+b[k+1],a[k+1]+b[k])
min(a[k]+b[k+1],a[k+1]+b[k])
而对于m个序列而言,我们可以将一,二序列算出的答案再和第三个序列进行计算,再和第四,五,等等以此类推,最终得到答案。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<int, 3> ar;
int mod = 1e9+7;
// const int maxv = 4e6 + 5;
// #define endl "\n"
void solve()
{
int m,n;
cin>>m>>n;
vector<vector<int> > a(m+5,vector<int> (n+5));
vector<int> ans(n+5);
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
cin>>a[i][j];
if(i==1) ans[j]=a[i][j];
}
}
sort(ans.begin()+1,ans.begin()+1+n);
for(int i=2;i<=m;i++){//m个序列,总共计算m-1次
priority_queue<pll,vector<pll> ,greater<pll>> q;
auto b=a[i];
for(int j=1;j<=n;j++) {
q.push({ans[1]+b[j],1});
}
vector<int> c(n+5);
for(int j=1;j<=n;j++){
auto [s,id]=q.top();
q.pop();
c[j]=s;
q.push({s-ans[id]+ans[id+1],id+1});
//即把接下来可能合法的值放入队中即可,优先队列会自动把最小的放前面
}
ans=c;
}
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
cout<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}