AtCoder Beginner Contest 123 D

题目链接

题目大意:

给出a,b,c三个数组,数组长度分别为x,y,z(1<=x,y,z<=1000),从这三个数组各挑选一个元素相加可以有x*y*z种可能,现在让你输出前k(k<=min(3000,x*y*z))个大的情况。

分析:

如果不给出限制的话,就直接遍历一遍,但是x*y*z现在明显是会超时的,但是k是小于3000的,所以这道题的解决方法是广搜。首先,确定起点,那么必定是三个数组最大的相加,那么应该怎么走呢?明显的,题目要求从大到小输出,比起点小的有哪些情况,有三种,第一种a的第二大+b的第一大+c的第一大,第二种a的第一大+b的第二大+c的第一大,第三种a的第一大+b的第一大+c的第二大,想到了这三种情况,那么路径就已经规划好了,但到底该选哪条呢?这里就要用到优先队列,每次选择最大的就可以了。

代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<cmath>
#include<map>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1000+5,dx[]={0,0,1},dy[]={0,1,0},dz[]={1,0,0};
bool cmp(ll x,ll y)//从大到小
{
    return x>y;
}
struct P{
    int x,y,z;
    ll w;
    P(){};
    P(int a,int b,int c,ll ww)
    {
        x=a;y=b;z=c;w=ww;
    }
    friend bool operator<(const P &p1,const P &p2)//从大到小
    {
        return p1.w<p2.w;
    }
};
priority_queue<P>q;
ll a[N],b[N],c[N];
int k;
map<ll,int>mmp;
ll zh(int x,int y,int z)//将x,y,z转换成一个长整型,用来映射
{
    return x*100000000+y*10000+z;
}
int x,y,z;

void bfs()
{
    mmp[0]=1;
    q.push(P(0,0,0,a[0]+b[0]+c[0]));
    while(!q.empty()&&k)
    {
        P p=q.top();
        q.pop();
        k--;
        printf("%lld\n",p.w);
        for(int i=0;i<3;i++)
        {
            int nx=p.x+dx[i],ny=p.y+dy[i],nz=p.z+dz[i];
            if(nx<x&&ny<y&&nz<z&&!mmp[zh(nx,ny,nz)])
            {
                mmp[zh(nx,ny,nz)]=1;
                q.push(P(nx,ny,nz,a[nx]+b[ny]+c[nz]));
            }
        }
    }
    while(!q.empty()){
        q.pop();
    }

}
int main()
{

    scanf("%d%d%d%d",&x,&y,&z,&k);
    for(int i=0;i<x;i++)
    {
        scanf("%lld",&a[i]);
    }
    for(int i=0;i<y;i++)
    {
        scanf("%lld",&b[i]);
    }
    for(int i=0;i<z;i++)
    {
        scanf("%lld",&c[i]);
    }
    sort(a,a+x,cmp);
    sort(b,b+y,cmp);
    sort(c,c+z,cmp);
    bfs();
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值