选择困难症

题目描述
又到吃饭时间,Polo 面对饭堂里琳(fei)琅(chang)满(keng)目(die)的各种食品,又陷入了痛苦的抉择中:该是吃手(jiao)打肉饼好呢,还是吃豆(cai)角(chong)肉片好呢?嗯……又不是天秤座怎么会酱紫呢?
具体来说,一顿饭由M 个不同的部分组成(荤菜,素菜,汤,甜品,饮料等等),Polo 要在每个部分中选一种作为今天的午饭。俗话说的好,永远没有免费的午餐,每种选择都需要有一定的花费。长者常常教导我们,便宜没好货,最便宜的选择估计比较坑爹,可囊中羞涩的Polo 还要把钱省下来给某人买生日礼物,这该怎么办呢?
于是一个折中方案出来了:第K 便宜的组合要花多少钱?这就要靠你了。
输入
第一行两个数M,K,含义如上所述。
接下来M 行,先是一个整数Ai,表示第i 个部分有多少种选择。接下来用空格分开的Ai 个整数表示每种选择的价格。

输出
一行一个整数表示答案。
样例输入
2 2
2 1 3
2 2 2
样例输出
3

提示
【样例解释】
最便宜的选择是第一部分选择1 块钱的,第二部分选择2 块的。但由于第二部分里2 块钱有两种不同的选择,所以第二便宜的总花费仍然是3 块。
这里写图片描述

Solution

此题跟poj2442是一样的,以前也做过,但考试的时候却一直想不出做法。今天又整理了一下。
首先要一行一行来,设运行到i行。
s[j]1(i1)j
然后对于每行,要初始化一些数,分别把第i小的和该行第1个加起来。

void prepare(int i)
{
    tot=0;
    len=0;
    for(int j=1;j<=x;j++) 
    {
        tot++;
        heap[tot].l=j; //l表示它是s数组中第l小的
        heap[tot].r=1; //r表示它在该行取了第r个
        heap[tot].v=s[j]+a[i][1];
        up(tot);
    }
}

然后一直取最小的,(必须直接取,因为指针往后面移动的话显然更大)。然后那个最小的r指针+1,产生出一个新的数。

            len++;
            t[len]=heap[1].v;
            if(heap[1].r+1<=a[i][0]) //r还能往后加1
            {
                tot++;
                heap[tot].l=heap[1].l;
                heap[tot].r=heap[1].r+1;
                heap[tot].v=heap[1].v-a[i][heap[1].r]+a[i][heap[1].r+1];
            }

这样的好处就是,你只需要考虑r这个下标,并且不影响取m个最小值的正确性。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
int n,m,x,tot,len;
int a[11][500001];
ll s[100005],t[100005];
struct ty
{
    int l,r;
    ll v;
}heap[100005];
void up(int id)
{
    while(id/2>=1) 
    {
        if(heap[id].v<heap[id/2].v) 
        {
            swap(heap[id],heap[id/2]);
            id=id/2;
        }
        else break;
    }
}
void down(int id)
{
    while(id*2<=tot) 
    {
        int j;
        if(id*2+1>tot||heap[id*2].v<heap[id*2+1].v) j=id*2;
        else j=id*2+1;
        if(heap[id].v>heap[j].v) 
        {
            swap(heap[id],heap[j]);
            id=j;
        }
        else break;
    }
}
void prepare(int i)
{
    tot=0;
    len=0;
    for(int j=1;j<=x;j++) 
    {
        tot++;
        heap[tot].l=j;
        heap[tot].r=1;
        heap[tot].v=s[j]+a[i][1];
        up(tot);
    }
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) 
    {
        scanf("%d",&a[i][0]);
        for(int j=1;j<=a[i][0];j++) scanf("%d",&a[i][j]);
        sort(a[i]+1,a[i]+a[i][0]+1);
    }
    x=a[1][0];
    for(int i=1;i<=x;i++) s[i]=a[1][i];
    for(int i=2;i<=n;i++) 
    {
        prepare(i);
        while(1) 
        {
            len++;
            t[len]=heap[1].v;
            if(heap[1].r+1<=a[i][0]) 
            {
                tot++;
                heap[tot].l=heap[1].l;
                heap[tot].r=heap[1].r+1;
                heap[tot].v=heap[1].v-a[i][heap[1].r]+a[i][heap[1].r+1];
            }
            swap(heap[1],heap[tot]);
            tot--;
            down(1);
            x=max(x,len);
            if(len==m||tot==0) break;
        }
        for(int j=1;j<=x;j++) s[j]=t[j];
    }
    cout<<s[m];
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值