首页 > 试题广场 >

序列合并

[编程题]序列合并
  • 热度指数:872 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解

其中系数aj都是整数满足0≤aj1000且至少有两个系数严格大于0,分别将n=1,n=2,n=3n...代入以上函数可以得到一个无穷长度的整数序列,即用8个系数a7,a6...a0可以唯一确定一个无穷长度的整数序列,现在给出k个通过以上方法定义的无穷序列,你需要求出将这些序列所有数字放在一起后,第n小的数字是多少?


输入描述:

第一行包含一个整数k,1≤k≤104

接下来k行,每行包含8个整数a7,a6,.....a0,表示一个函数的系数,0≤aj≤1000

最后一行包含一个整数n,1≤n≤105



输出描述:

输出对应的答案,保证最后的答案不超过1017

示例1

输入

3
0 0 0 0 1 2 0 0
0 0 0 0 0 0 10 6
0 0 0 0 0 0 25 1
9

输出

51
由于每个序列的通项公式的系数是非负整数,且自变量n为正整数,所以每个序列都是n的增函数,那么思路很简单:
1. 维护一个容量为k的小根堆,令n=1,带入k个序列通项公式中得到k个值加入堆中。
2. 然后最小的那个数出堆,这个数一定属于某一个序列,它出队之后再根据这个序列的通项公式计算n=2的值加入堆,保证堆中始终有k个数在比较大小。
重复以上操作,直到弹出第n个数。注意在弹出一个数的时候,我们需要知道这个数属于哪个序列(以便对应上序列的系数),并且这个序列需要继续往堆中补数的话我们就还需要知道自变量n应该是多少。因此我们可以封装一个节点类,其中包含三个属性:1.这个数的值;2.产生这个数的序列编号;3.带入序列公式得到这个值的自变量。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Comparator;
import java.util.PriorityQueue;

class Node {
    public int n;        // 序列通项公式中的自变量
    public int idx;      // 序列编号
    public long val;     // 将自变量带入序列通项中算得的值
    public Node(int n, int idx, long val) {
        this.n = n;
        this.idx = idx;
        this.val = val;
    }
}

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int k = Integer.parseInt(br.readLine().trim());
        int[][] coefficient = new int[k][8];
        for(int i = 0; i < k; i++){
            String[] params = br.readLine().trim().split(" ");
            for(int j = 0; j < 8; j++)
                coefficient[i][7 - j] = Integer.parseInt(params[j]);
        }
        int n = Integer.parseInt(br.readLine().trim());
        // 构建一个容量为k的堆
        PriorityQueue<Node> heap = new PriorityQueue<>(k, new Comparator<Node>(){
            @Override
            public int compare(Node n1, Node n2){
                // 因为compare返回int,而Node.val类型为long,所以要改写成如下的形式
                if(n1.val > n2.val)
                    return 1;
                else if(n1.val < n2.val)
                    return -1;
                else
                    return 0;
            }
        });
        int count = 0;
        long topN = 0;
        // 先令n=1,带入k个序列公式计算出k个数加入堆
        for(int i = 0; i < k; i++)
            heap.offer(new Node(1, i, sequence(coefficient[i], 1)));
        // 到了第n小的数字就退出循环,打印第n小的数字
        while(count < n){
            Node cur = heap.poll();
            topN = cur.val;
            // 由cur.idx可以知道是哪个序列的数字出队,接下来需要在这个序列中补数
            cur.n ++;             // 自变量自增
            cur.val = sequence(coefficient[cur.idx], cur.n);   // 计算新的值
            heap.offer(cur);      // 向堆中补数
            count ++;
        }
        System.out.println(topN);
    }

    // 计算系数为params的序列在自变量为n时的值
    private static long sequence(int[] params, int n) {
        long val = 0;
        for(int i = 0; i < params.length; i++)
            val += params[i] * Math.pow(n, i);
        return val;
    }
}


编辑于 2021-02-26 22:21:19 回复(0)
多项式函数f(n) 在 aj>=0,n>=1时是一个递增函数
因此 k 个不同的序列可以看做是 k 个递增的有序数组
求第 n 小的数,只要做 k 路归并排序,第 n 个数就是第 n 小的数
#include<bits/stdc++.h>
using namespace std;

const int N = 8;
struct Node{
    long value;
    int id;
};

struct cmp{
    bool operator()(const Node& a, const Node& b){
        return a.value > b.value;
    }
};

int compute(vector<int>& seq, int n){
    //f=((((((a7*n +a6)*n +a5)*n +a4)*n +a3)*n +a2)*n +a1)*n +a0
    long ret = seq[0];
    for(int i=1; i<N; ++i){
        ret = ret*n + seq[i];
    }
    return ret;
}

int main(){
    int k=0, n=0;
    scanf("%d", &k);
    vector<vector<int>> seq(k, vector<int>(N, 0));
    for(int i=0; i<k; ++i){
        for(int j=0; j<N; ++j){
            scanf("%d", &seq[i][j]);
        }
    }
    scanf("%d", &n);
    // k路归并
    priority_queue<Node, vector<Node>, cmp> q;
    vector<int> next(k, 1);
    long val = 0;
    for(int i=0; i<k; ++i){
        val = compute(seq[i], next[i]);
        q.push({val, i});
        ++next[i];
    }
    while(--n){
        Node cur = q.top(); q.pop();
        val = compute(seq[cur.id], next[cur.id]);
        q.push({val, cur.id});
        ++next[cur.id];
    }
    // 第n个值
    cout<< q.top().value << endl;
    return 0;
}


编辑于 2019-08-19 19:28:06 回复(1)
#include<iostream>
#include<vector>
#include<math.h>
#include<algorithm>
using namespace std;

struct pnum{
    long long num;//存储计算值
    int pos;//存储系数位置
    int nnum;//存储计算得到num时n的值
    bool flag;//存储是否最小,下一次循环只计算上一次最小的那个
};
int main()
{
    int k,m;
    while(cin>>k)
    {
        vector<vector<int>> p(k,vector<int>(8,0));
        for(int i=0;i<k;i++)
        {
            for(int j=0;j<8;j++)
            {
                cin>>p[i][j];
            }
        }
        cin>>m;
        int num=0;
        pnum tt;
        tt.num=0;
        tt.flag=1;
        tt.pos=0;
        tt.nnum=0;
        vector<pnum> temp(k,tt);
        long long minval=10000;//每一次从这一轮计算种找到一个最小值
        for(int n=1;num<m;)//n从1开始取值,在上一个最小值的n的基础上增加1,直到最少计算出了m个数
        {
            for(int i=0;i<k;i++)
            {
                if(temp[i].flag)
                {
                    long long sum=0;
                    for(int j=0;j<8;j++)
                    { 
                        sum+=p[i][j]*pow(n,7-j);
                    }
                    temp[i].num=sum;
                    temp[i].pos=i;
                    temp[i].nnum=n;
                    temp[i].flag=0;
                }
            }
            int pos=0;minval=temp[0].num;
            for(int h=0;h<temp.size();h++)
            {
                if(temp[h].num<minval)
                {
                    minval=temp[h].num;
                    pos=h;
                }
            }
            //cout<<minval<<endl;
            num++;
            temp[pos].flag=1;
            n=temp[pos].nnum+1;
        }
        cout<<minval<<endl;
    }
    return 0;
}


编辑于 2020-11-22 14:27:07 回复(0)
AC了,思路比较一般,都在注释里了
import java.util.*;
public class Main{
    public static long get(int[] a,int n){//求多项式a,自变量是n时的值
        long s=0;
        for(int i=0;i<=7;i++){
            if(a[i]==0)
                continue;
            s+=(long)Math.pow(n,7-i)*a[i];
        }
        return s;
    }
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int k=sc.nextInt();
        int[][] a = new int[k][8];
        for(int i=0;i<k;i++){
            String[] str=sc.nextLine().split(" ");
            for(int j=0;j<8;j++)
                a[i][j]=sc.nextInt();
        }
        int n=sc.nextInt();

        int[] flag=new int[k];//存储个列走动到哪里了,取值1到无穷,与value数组配合存当前个多项式值,减少运算
        for(int i=0;i<k;i++)//开始多项式自变量都取1
            flag[i]=1;
        long[] value=new long[k];//存储各列当前值
        for(int i=0;i<k;i++){
            value[i]=get(a[i],flag[i]);
        }

        long res = 0;//存储排序值
        for(int i=0;i<n;i++){
            long min=Integer.MAX_VALUE;
            int idx=0;//当前值最小的序列
            for(int j=0;j<k;j++){
                if(value[j]<min){
                    min=value[j];
                    idx=j;
                }
            }
            res=min;
            flag[idx]++;
            value[idx]=get(a[idx],flag[idx]);
            
        }
        System.out.println(res);
    }
}
发表于 2020-03-28 16:23:20 回复(0)
我的代码本机可以通过样例,但是上传到牛客网上出现
请检查是否存在数组越界等非法访问情况
case通过率为0.00%
不太清楚为什么。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Scanner;

public class Main {
	static class Node{
		Node next=null;
		int val;
		public Node(int val) {
			this.val=val;
		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner scanner = new Scanner(System.in);
		int k =  scanner.nextInt();
		ArrayList<Integer> arr = new ArrayList<Integer>();
		for(int i=0;i<k;i++) {
			for(int j=0;j<8;j++) {
				arr.add(scanner.nextInt());
			}
		}
		int n= scanner.nextInt();
		scanner.close();
		Node head=null,curNode=null,preNode=null;
		int count=0;
		int[] curNums = new int[k];
		Go:
		for(int m=1;m<=n;m++) {
			Iterator<Integer> it = arr.iterator();
			for(int i=0;i<k;i++) {
				int cur=0;
				for(int j=0;j<8;j++) {
					cur+=(int)it.next()*Math.pow(m, 7-j);
				}
				curNode = new Node(cur);
				if(head ==null) {
					head=curNode;
					count++;
				}else {
					Node temp = head;
					if(cur>=head.val&&count<n) {
						head = new Node(cur);
						head.next=temp;
						count++;
					}else if(cur<head.val){
						while(temp.val>cur&&temp!=null) {
							preNode=temp;
							temp=temp.next;
						}
						curNode.next=temp;
						preNode.next=curNode;
						if(count>=n) {
							head=head.next;
							Arrays.sort(curNums);
							if(curNums[0]>=head.val) break Go;
						}
						count++;
					}   
				}
				if(count>=n-1) {
					curNums[i]=cur;
				}
			}
		}
		System.out.println(head.val);

	}

}


发表于 2019-08-26 22:31:00 回复(0)

问题信息

上传者:小小
难度:
5条回答 2872浏览

热门推荐

通过挑战的用户

查看代码