USACO Training Section 3.1 Humble Numbers

英文原题  中文题译

大意:给定若干素数p1,..pk,找第N个素因子全为这些已知素数的合数。

又是一开始定错了方向,开始了整晚的折磨。唉。最后,完整的过了。

一上来,分析算法时就确定用优先队列找已有的最小的数,把接下来的乘积放如队列中。接着噩梦开始了。马上发现,数可能重复,而优先队列中无法排除重复数,于是想自己写一个可以清除重复数的优先队列,结果发现写不出来。倒是写了个二项堆模板。

无奈之下,用set来记录已经插入的数。依然错,发现输出的是负数,显然是整数越界。弄了很久才想起来long long next=cur*primes[i];之后判断next是否越界是无效的,必须先转换类型(晕,我是猪)。

排除此问题之后,在最后一个测试实例(最大的一个)上发现空间溢出,看了看,用了14M空间。于是,在扩展了N个数之后记录最大值,超过最大值的肯定不是要找的数。这样下来,空间变成8M,不越界了。

但是,时间是1.67秒,超过了1秒的限制。换用自己的Heap,时间差不多。很无奈之下下去吹了吹冷风,想:难道就这样挂了?一定要换方法吗?

最后逐行想过去,vis.find(next)==vis.end() && next<limit,就是它了, 交换了比较顺序,变成next<limit && vis.find(next)==vis.end(),时间立马降低一半,变成0.84秒,通过了。这也算是个常识性错误,唉。改晕头了才会写出这样的代码。也可见set的实现是很烂的,集合越大,性能越差。

/*
ID: blackco3
TASK: humble
LANG: C++
*/
#include <iostream>
#include <queue>
#include <set>
using namespace std;
#define _max_ 100

template < typename t_heap_value, int _max_heap_size_ >
class Heap {
	t_heap_value heap[_max_heap_size_+1];
	int size ;
private :
	inline void swap( int i, int j ){
		t_heap_value tmp ;
		tmp=heap[i], heap[i]=heap[j], heap[j]= tmp ;
	}
	inline void del( int pos){
		heap[pos]=heap[size--];
		while( (pos<<1)<=size ) {
			int left=pos<<1, right=left+1 ;
			if( right>size || heap[left]<heap[right] )
				if( heap[left]<heap[pos] )
					swap( pos, left ), pos=left ;
				else
					break ;
			else 
				if( heap[right]<heap[pos] )
					swap( pos, right ), pos=right ;
				else
					break ;
		}
	}
public :
	Heap(){ size=0; } ;
	t_heap_value top(){ return heap[1]; } ;
	int empty(){ return size>0; } ;
	void pop(){ 
		if(size)
			del(1);
	}; 
	void push(t_heap_value v ){
		heap[++size] = v ;
		for( int pos=size; pos>0 ; pos >>= 1 ) {
			if( heap[pos>>1]>heap[pos] )
				swap(pos, pos>>1);
			else 
				break ;
		}
	}
	void show() {
		for( int i=1; i<=size; i++ )
			cout << heap[i] << " ";
		cout << endl ;
	}
};

Heap<int,1000000> heap;
set<int> vis;

int main() {
	freopen("humble.in", "r", stdin);
	freopen("humble.out", "w", stdout);

    int n_prime, n_th, primes[_max_] ;
    cin >> n_prime >> n_th ;
	for( int i=0; i<n_prime; i++ ){
		cin >> primes[i] ;
	}
	heap.push(1);
	for( int i=0, limit=0, count=0; i<n_th; i++ ){
		register int cur=heap.top();
		heap.pop();
		for( int i=0; i<n_prime; i++ ){
			long long next=((long long)cur)*primes[i];
			if( next > 0x7fffffff )
				continue ;
			if( count<n_th ){
				if( vis.find(next)==vis.end() ){
					vis.insert(next), heap.push(next);
					count++, limit=limit>next? limit : next ;
				}
			} else {
				if( next<limit && vis.find(next)==vis.end()  )
					vis.insert(next), heap.push(next);
			}
		}
	}
	cout << heap.top() << endl ;
	return 0;
}


补充,重写了一个暴力枚举的,时间上比之前的好。用一个数组记录每个素数上一次乘的数的位置和值,这样,每个乘法只做一次。用这种方式还可以记录出指数和(参见邮票Stamps一题)。

/*
ID: blackco3
TASK: humble
LANG: C++
*/
#include <iostream>
using namespace std;
#define _max_prime_ 100
#define _max_val_ 100001
int main() {
	freopen("humble.in", "r", stdin);
	freopen("humble.out", "w", stdout);

    int n_prime, n_th, primes[_max_prime_], pos[_max_prime_], pval[_max_prime_] ;
    cin >> n_prime >> n_th ;
	for( int i=0; i<n_prime; i++ ){
		cin >> primes[i] ;
		pos[i]=0, pval[i]=primes[i] ;
	}
	int val[_max_val_]={1} ;
	for( int ival=1; ival<=n_th; ival++ ){
		register int min_val=0x7fffffff, min_pos ;
		do{
			min_val=0x7fffffff ;
			for( int x=0; x<n_prime; x++ )
				if( pval[x] < min_val )
					min_val=pval[x], min_pos=x ;
			if( min_val > val[ival-1] )
				break ;
			pval[min_pos] = val[++pos[min_pos]]*primes[min_pos] ;
		}while( true )  ;
		val[ival]=min_val ;
	}
	cout << val[n_th] << endl ;
	return 0;
}

你可能感兴趣的:(算法,J#,asp)