首页 > 其他 > 详细

Poj2442 Sequence

时间:2019-10-15 17:51:35      阅读:94      评论:0      收藏:0      [点我收藏+]

Poj2442 Sequence
给定M个长度为N的序列,从每个序列中任取一个数求和,可以构成 N^M 个和,求其中最小的N个和。
N≤2000,M≤100。
Input
第一行给出数据的组数
每组数据先给出M,N.(0 < m <= 100, 0 < n <= 2000).
接下来M行N列,描述这个数字矩形,每个数字<=10000
Output
如题
Sample Input
1
2 3
1 2 3
2 2 3
Sample Output
3 3 4

 Sol1:

先简单想一下,如果数据只有一行的话,那很明显开一个小根堆,将元素值丢进去,再输出就好了。但问题是数据有多行,于是我们一行行处理。假设它只有两行,于是我们可以将第一行数据所在的小根堆中的元素一个个拿出来。然后让它加上第二行的每一行个数字,然后设计一个大根堆。为什么是大根堆呢,这里要想清楚。
因为我们的目的是求出其和最小的N个元素,设计成一个大根堆的话,如果当前堆顶元素+第二行某个元素的和小于大根堆的堆顶值,则说明这个堆顶值是没有必要存在的,要T掉。如果设计成一个小根堆的,则不能达到这个目的。于是我们维护一个只包含N个元素的大根堆。当上面的过程全处理结束后,再将大根堆的值倒入小根堆就好了。这样就得到了前两行的,符合题意的数值了。
接下来再一行行处理就好了。

   
#include <bits/stdc++.h>
#include <queue>
using namespace std;
int n,m,a[4000005],b[4000005];
int main()
{
    int t,flat,i,j,sum,k;
    scanf("%d",&t);
    while(t--)
    {
        int ai=1,bi=0,x,r,sum,a[2005];
        scanf("%d%d",&n,&m);
        priority_queue <int ,vector<int >,greater<int> >p;//小根堆 
        priority_queue <int ,vector<int >,less<int> >q;//大根堆 
        for(i=0; i<m; i++)
        {
            scanf("%d",&x);
            p.push(x);
        }
        for(i=1; i<n; i++)
        {
            for(j=0; j<m; j++)
            {
                scanf("%d",&a[j]);
            }
            while(!p.empty()) //从小根堆中一个个取出数字
            {
                sum=p.top();
                p.pop();
                for(j=0;j<m;j++)//将sum与第i行的每个数字进行相加
                {
                    if(q.size()==m&&q.top()>sum+a[j]) 
     	         //控制大根堆中只有M个元素,且值尽可能的小 
                    {
                        q.pop();
                        q.push(sum+a[j]);
                    }
                    else if(q.size()<m)
                    //如果大根堆的元素值没达到M个,就直接丢进去
                    {
                         q.push(sum+a[j]);
                         
                    }
                }
            }
            while(!q.empty())//将大根堆的元素值一个个丢到小根堆中
            {
                p.push(q.top());
                q.pop();
            }
        }
        flat=0;
        for(i=0;i<m;i++)
        {
            if(flat)
            {
                printf(" ");
            }
            printf("%d",p.top());
            p.pop();
            flat=1;
        }
        printf("\n");
    }
    return 0;
}

  

//题后记
用这种方法来做有序表的求和那个题要跑20+s,但其实正常的方法10s是可以过的,因为这种方法常数太大了(对堆的各种操作太多了),正解可以看lyd书上。有个方法比这个难理解一点,但常数要小一些

Sol2:
下面这个做法非常好,利用数据的有序化,缩短时间
大致思路是:将第一行加到堆内,将第二行的数字升序排好。然后将堆中数字降序放到数组a中
将a数组的第一个数字加上第二行每个数字,结果放到一个堆中
拿出第二行的每个数字,假设为b,让b加上a数组中每个数字
(一定是倒过来加,保证加出来的数字越来越大,如果加出来的数字比大根堆的堆顶小就代替之,否则说是加不下去了直接break;

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;
priority_queue<int>que;
int m,n;
int num[1111][2222];
int a[2222];
int main()
{
	#ifndef ONLINE_JUDGE
		freopen("G:/1.txt","r",stdin);
		freopen("G:/2.txt","w",stdout);
	#endif
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&m,&n);
		for(int i=1;i<=m;i++)
		{
			for(int j=1;j<=n;j++)
			{
				scanf("%d",&num[i][j]);
			}
		}
	
		for(int i=1;i<=n;i++)  //将第一行的数字压入堆 
		{
			que.push(num[1][i]);
		}
		for(int i=2;i<=m;i++) //从第二行到最后一行 
		{
	    	sort(num[i]+1,num[i]+1+n);  
			//升序排好 ?为什么呢?因为我们是要尽量取小值,所以控制最先入堆的数字值尽可能小一点
			//后面再找出来的数字,如果大于它就直接break了.不错不错 
			for(int j=1;j<=n;j++)
            //将堆中的数字放到数组,数组中的值是从大到小的,弹完后注意堆为空了
			{
				a[j]=que.top();
				que.pop();
			}
			for(int j=1;j<=n;j++)
			 //将堆中数字加上当前行的第一个数字,这样的结果明显是符合题意的 
			{
				que.push(num[i][1]+a[j]);
			}
			for(int j=2;j<=n;j++) //当前行的第二个数字到最后一个数字 
			{
				for(int k=n;k>=1;k--)  
				// 与a数组的数字一个个加,注意这是个逆循环,也就是说所加的a[k]的值会越来越大 
					if(num[i][j]+a[k]<que.top())
					{
						que.pop();
						que.push(num[i][j]+a[k]);
					}
					else //没有必要加下去了,因为后面要加的a[k]的值越来越大了 
					{
						break;
					}
			}
		}
		for(int i=1;i<=n;i++)
		{
			a[i]=que.top();
			que.pop();
		}
		for(int i=n;i>=2;i--)
		{
			printf("%d ",a[i]);
		}
		printf("%d\n",a[1]);
	}
}

 

 

Poj2442 Sequence

原文:https://www.cnblogs.com/cutemush/p/11679069.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!