想她一次就背十个单词,当我英语过六级后,我就去告诉她,我很在意她
一天一道数论题,当我可以秒杀数论题的时候,就开始做 DP
今日份快乐:洛谷 P2085 传送门
明天份快乐:codeforces 1355C 传送门
题目大意
有 n 个二次函数,给出每个函数的系数 a、b 和 c,当 x > 0 时,请求出所有函数的所有函数值中最小的 m 个。
数据范围:
分析
题目的数据范围降低了题目难度,a、b、c 和 x 都为正整数,说明给出的 n 个函数都在 1 到正无穷大上单调递增。对于每一个函数,我们可以定义一个指针,让它指向 x = 1,这个时候的 y 值就是当前函数的最小值,我们把所有函数的最小值进行排序。每次取出所有函数的最小值中的最小值,并让这个函数的指针右移,右移以后再次排序,…………无限循环直到找到 m 个最小值。
我们需要一直维护所有函数的最小值,这个时候需要用到优先队列。
稍微讲一下优先队列
优先队列具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的
默认为升序,队列中的所有数据的最小值在队首
priority_queue< int >qu 就是一个储存 int 类型的优先队列
- qu.top(); 可以返回队列的第一个元素,也就是所有元素的最小值
- qu.pop(): 可以删除队列的第一个元素
- qu.push(x);在队列中添加元素 x
这是本题要用到的三个函数,细节看代码
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
typedef struct Node{ // 定义一个数据类型,具有二次函数的系数、自变量和函数值
int a;
int b;
int c;
int x; // 分析中提到的指针
int y; // 指针对应的函数值
} node;
bool operator < (const node &A, const node &B){ // 重载 < 号,让优先队列按照 y 的值进行排序
return A.y > B.y;
}
int f(node z, int x){ // 计算 x 对应的函数值
return z.a * x * x + z.b * x + z.c;
}
int main(){
ios::sync_with_stdio(false);
int n, m;
cin >> n >> m;
node v;
priority_queue<node>qu; // 优先队列
for(int i = 1; i <= n; i++){
cin >> v.a >> v.b >> v.c;
v.x = 1; // 默认 x = 1
v.y = v.a + v.b + v.c;;
qu.push(v); // 把这个二次函数加到队列中
}
vector<int>res; // 放答案
int ans = 0; // 答案个数
while(ans <= m){
node now = qu.top(); // 取出队首元素
qu.pop(); // 因为要更新这个函数,把原来的函数删去
res.push_back(now.y); // 记录答案
ans++; // 更新答案个数
now.x++; // 指针右移
now.y = f(now, now.x); // 更新函数
qu.push(now); // 更新队列,重新排序
}
for(int i = 0; i < m; i++) cout << res[i] << " ";
return 0;
}
坚持的时候很狼狈,等成功以后,丑的还是丑的🤭