信息学奥赛一本通 1370:最小函数值 | 洛谷 P2085 最小函数值

【题目链接】

ybt 1370:最小函数值
洛谷 P2085 最小函数值

【题目考点】

1. 堆

【解题思路】

输入要求中有:“以下n行每行三个正整数,其中第i行的三个数分别位 A i A_i Ai B i B_i Bi C i C_i Ci。”,
因此,可知二次函数的二次项系数a,一次项系数b,常数项c都是正整数。
由于 a > 0 a>0 a>0,因此二次函数开口向上,顶点位置横坐标为 − b 2 a -\frac{b}{2a} 2ab,已知本题中a,b都为正整数,那么二次函数顶点横坐标在负半轴。那么在x>0的范围内,二次函数是增函数。已知 x ∈ N ∗ x\in N^* xN,即x是正整数,因此所有的函数都是增函数。
对于该函数来说,x=1时函数值最小,x=2时函数值第二小。。。
也就是说,在取 F ( 1 ) F(1) F(1)前,一定不会取到 F ( 2 ) F(2) F(2)。我们可以先把所有函数x=1时的值加入到一个集合中,取出其中的最小值,然后让生成该值函数的自变量加1,把求出函数值放到这个集合中。为了能够高效取出最小值,我们使用堆这一数据结构。具体做法如下:
设Func类,包含属性:二次项系数a,一次项系数b,常数项c以及当前自变量x。将x代入函数 F ( x ) = a x 2 + b x + c F(x)=ax^2+bx+c F(x)=ax2+bx+c可以求出函数值,每个函数类对象都可以求出唯一的函数值。
设堆pq,堆中保存的是Func类对象,堆中的优先级定义为:Func类对象的函数值越小,优先级越高。
先将各函数生成Func类对象,每个对象的x都为1。
循环m次,每次循环取堆顶,获得各Func类对象中的最小函数值,输出该值。而后删除堆顶,让该Func类对象离开堆,接着让该对象的x加1,再把这个新的对象插入堆。

【题解代码】

写法1:使用priority_queue类

#include <bits/stdc++.h>
using namespace std;
struct Func//表示一个函数 
{
    
    
    int a, b, c, x;
    Func(int _a, int _b, int _c):a(_a), b(_b), c(_c)
	{
    
    
		x = 1;
	}
    Func(){
    
    }
    int getVal()//求函数值 
    {
    
    
        return a*x*x+b*x+c;
    }
};
struct Cmp
{
    
    
	bool operator () (Func &a, Func &b)
	{
    
    
		return b.getVal() < a.getVal();//优先条件为:函数值更小的Func对象更优先
	}
};
int main()
{
    
    
	priority_queue<Func, vector<Func>, Cmp> pq;
    int n, m, a, b, c;
    cin >> n >> m;
    for(int i = 1; i <= n; ++i)
    {
    
    
        cin >> a >> b >> c;
        pq.push(Func(a, b, c));
    }
    for(int i = 1; i <= m; ++i)
    {
    
    
    	Func f = pq.top();//f表示一个函数 
    	pq.pop();
        cout << f.getVal() << ' ';
        f.x++;//f的x值增加1 
        pq.push(f);
    }
    return 0;
}

写法2:手写堆

#include <bits/stdc++.h>
using namespace std;
#define N 10005 
struct Func//表示一个函数 
{
    
    
    int a, b, c, x;
    Func(int _a, int _b, int _c):a(_a), b(_b), c(_c)
	{
    
    
		x = 1;
	}
    Func(){
    
    }
    int getVal()//求函数值 
    {
    
    
        return a*x*x+b*x+c;
    }
};
template<class T, class IsPriorClass>
class PriorityQueue//优先队列类 
{
    
    
private:
    T heap[N];
	int n;
	IsPriorClass isPrior;//仿函数对象:用于比较两元素的优先级 
    void shiftUp(int i)//第i结点上移 
    {
    
    
        if(i == 1)//p是根结点 
            return;
        if(isPrior(heap[i/2], heap[i]))
        {
    
    
            swap(heap[i], heap[i/2]);
            shiftUp(i/2);
        }
    }
    void shiftDown(int i)//第i结点下沉 
    {
    
    
        if(i > n/2)//如果i是叶子结点
            return; 
        int sel;//选择交换的结点位置 
        if(2*i+1 <= n && isPrior(heap[2*i],heap[2*i+1]))//有右孩子且右孩子值更大 
            sel = 2*i + 1;
        else//没有右孩子或左孩子值更大 
            sel = 2*i; 
        if(isPrior(heap[i],heap[sel]))
        {
    
    
            swap(heap[sel], heap[i]);
            shiftDown(sel); 
        }
    }
public:
    PriorityQueue()
    {
    
    
        n = 0;
    }
    void push(T val)//堆中插入元素 
    {
    
    
        heap[++n] = val;
        shiftUp(n); 
    } 
    void pop()//删除堆顶 
    {
    
    
        swap(heap[1], heap[n]);
        n--;
        shiftDown(1); 
    } 
    T top()//获取堆顶 
    {
    
    
        return heap[1];
    }
    bool empty()//判断堆为空 
    {
    
    
        return n == 0;
    }
    int size()//获取堆中元素个数 
    {
    
    
    	return n;
	}
};
struct Cmp
{
    
    
	bool operator () (Func &a, Func &b)
	{
    
    
		return b.getVal() < a.getVal();//优先条件为:函数值更小的Func对象更优先
	}
};
int main()
{
    
    
	PriorityQueue<Func, Cmp> pq; 
    int n, m, a, b, c;
    cin >> n >> m;
    for(int i = 1; i <= n; ++i)
    {
    
    
        cin >> a >> b >> c;
        pq.push(Func(a, b, c));
    }
    for(int i = 1; i <= m; ++i)
    {
    
    
    	Func f = pq.top();//f表示一个函数 
    	pq.pop();
        cout << f.getVal() << ' ';
        f.x++;//f的x值增加1 
        pq.push(f);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lq1990717/article/details/128391432
今日推荐