寒假训练2018.1.31训练日志------贪心基础学习(优先队列,priority_queue)

本文通过解决果园果子合并的问题,介绍了如何利用贪心算法优化合并顺序以减少体力消耗。并展示了使用不同排序方法(如插入排序、优先队列)来提高算法效率的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    总感觉今天的效率特别低,看了一天的课本和ppt,被几个例题弄的晕头转向的。好不容易临近傍晚把基础知识看完了一遍,还想着晚上可以刷一部分题目,可是没想到一个题我做了一晚上,直到现在,才算是稍微弄懂了点吧。(其实感觉这题困扰我的地方好像和贪心一点关系都没有QAQ)。

题目描述

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。

输入输出格式

输入格式:

输入文件fruit.in包括两行,第一行是一个整数n(1<=n<=10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。

输出格式:

输出文件fruit.out包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于2^31。

输入输出样例

输入样例#1: 复制
3 
1 2 9 
输出样例#1: 复制
15




说明

对于30%的数据,保证有n<=1000:

对于50%的数据,保证有n<=5000;

对于全部的数据,保证有n<=10000。


刚拿到这题,很容易想到只要每次取两个最小值就好了。而要保证每次都取到的是最小值只要每次合并完成后升序操作就好了。所以第一次的代码如下:

#include<bits/stdc++.h>
using namespace std;
int a[10001],sum=0;
void solve(int a[],int x,int y)
{

    if(x==y)return ;
    sort(a+x,a+y);
    sum+=a[x]+a[x+1];
    a[x+1]+=a[x];
    solve(a,x+1,y);
}
int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;++i)cin>>a[i];
    solve(a,1,n);
    cout<<sum;
    return 0;

}


#1AC0ms/2046KB
 
#2TLE
 
#3WA
 
#4WA
 
#5WA
 
#6WA
 
#7TLE
 
#8TLE
 
#9TLE
 
#10TLE



很明显,如果每次合并完都排序的话时间复杂度o(n*n*nlogn),n取10000,很容易就超时。之后改用了一个插入排序,时间复杂度明显

就降低了。

第二次提交代码:

#include<bits/stdc++.h>
using namespace std;
int a[10001],sum=0,n;
void solve(int a[],int x)
{
   if(x==n)return ;
   sum+=a[x]+a[x+1];
   a[x+1]+=a[x];
   if(a[x+1]>a[x+2])   //这里判断不能省
   {
       int t=a[x+1],i=x+2;      
       for(;a[i]<t&&i<=n;++i)    //插入排序开始用while写,但感觉实现起来太复杂,用for关键注意当a[i]<t不成立时,i不会在++了,此时i的位置是不满足条件的第一个点。   
       {
           a[i-1]=a[i];
       }
       a[i-1]=t;
   }
   solve(a,x+1);
}
int main()
{
                    /*freopen("3.text","r",stdin);
                    freopen("1.text","w",stdout);*/
    cin>>n;
    for(int i=1;i<=n;++i)cin>>a[i];
    sort(a+1,a+1+n);
    solve(a,1);
    cout<<sum;
    return 0;

}

#1AC0ms/2042KB
 
#2AC140ms/2492KB
 
#3AC0ms/1949KB
 
#4AC0ms/2089KB
 
#5AC16ms/2210KB
 
#6AC36ms/2304KB
 
#7AC96ms/2542KB
 
#8AC140ms/2593KB
 
#9AC96ms/2570KB
 
#10AC140ms/2492KB



这次虽然A了,不过还是想再用优先队列(自动排序)来做一遍。

之前对优先队列不太熟悉,所以今晚花了一晚上钻研它,真的钻了好久 重载 的牛角尖。



其实看了一晚上无非就这么几个知识点

//1、基本操作:
q.size();//返回q里元素个数

q.empty();//返回q是否为空,空则返回1,否则返回0

q.push(k);//在q的末尾插入k

q.pop();//删掉q的第一个元素

q.top();//返回q的第一个元素

q.back();//返回q的末尾元素

//2、常用定义形式:

priority_queue <int,vector<int>,less<int> > p;//从大到小排列

priority_queue <int,vector<int>,greater<int> > q;//从小到大排列

最后附上最后一次的代码(这样写真的好简单):

#include<bits/stdc++.h>
using namespace std;
priority_queue < int ,vector<int>,greater<int> > a;//这东西一定要记住,太好用了。
int main()
{
    int n,m;cin>>n;
    for(int i=1;i<=n;++i)
    {
        cin>>m;
        a.push(m);
    }
    int sum=0,x,y;
    for(int i=1;i<=n-1;++i)
   {
        x=a.top();a.pop();
        y=a.top();a.pop();
        sum+=x+y;
        a.push(x+y);
   }
   cout<<sum;
   return 0;
}

#1AC0ms/2074KB
 
#2AC20ms/2265KB
 
#3AC0ms/2156KB
 
#4AC0ms/2167KB
 
#5AC8ms/2218KB
 
#6AC8ms/2234KB
 
#7AC20ms/2253KB
 
#8AC20ms/2234KB
 
#9AC20ms/2175KB
 
#10AC20ms/2253KB

最后,今天还发现了一个.stable_sort(),比sort的稳定性要好,尤其在对结构体排序时。

明天将会是忙碌的一天。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值