codeforces Round #669 (Div. 2) 1407B Big Vova

题目链接

在这里插入图片描述

题目翻译:

给你n个正整数a1,a2,…,an。你需要使用这些数组成一个数列b1,b2,…,bn,每个数只能用一次,且必须所有的数都被用到。另外ci表示GCD(b1,…,bi),即前i个数的最大公约数。组成的数列b要使得数列c1,c2,…,cn字典序最大。
当且仅当数列a和b满足下列任一条件时,数列a的字典序小于数列b:

  • a是b的前缀,但是a≠b。
  • 依次比较两个数列,两个数列第一次不相同是在下标为i的地方,且ai小于bi
解题思路:

首先我们可以确定,第一个数一定是最大的数(因为其最大公约数就是其本身),所以我们把最大的数排到第一位。然后,在已经确定前k-1个数的情况下,我们要怎么选择第k个数?当然是找到一个数使得前k个数的最大公约数最大的。问题是怎么找更方便,快速。比如我们要怎么得出4,6,8的最大公约数,我们可以先求出4和6的最大公约数2,再求出2和8的最大公约数2,最后得出的结果就是2。即求前k个数的最大公约数,也就是求前k-1个数的最大公约数第k个数的最大公约数。所以在确定前k-1个数,选择第k个数的时候,我们可以遍历剩下的数,找出一个数,使得前k-1个数的最大公约数第k个数的公约数最大。

代码:
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<cstdio>
#include<cmath>
using namespace std;
const int N = 1010;
int t,n,a[N];
vector<int>v; 
void solve(){
    
    
	v.clear();
	cin>>n;
	for(int i=0;i<n;i++){
    
    
		cin>>a[i];
	}
	sort(a,a+n);
	v.push_back(a[n-1]);
	int sum=n-1,c=a[n-1],ind=n-1;
	a[n-1]=0;
	while(sum--){
    
    
		int cur_max = 0;
		for(int i=0;i<n;i++){
    
    
			if(a[i]&&__gcd(c,a[i])>=cur_max){
    
    
				cur_max=__gcd(c,a[i]);
				ind = i;
			}
		}
		v.push_back(a[ind]);
		a[ind]=0;
		c = cur_max;
	}
	for(int i=0;i<n;i++){
    
    
		cout<<v[i]<<" ";
	}
	cout<<endl;
	return ;
}
int main(){
    
    
//	freopen("1.txt","r",stdin);
	cin>>t;
	while(t--){
    
    
		solve();
	}
	return 0;
}
我的思路:

我写的就比较复杂,主要是没想到前k-1个数的最大公约数第k个数的最大公约数就是前k个数的最大公约数这一点。
先求出最大数的所有因数,存入v,然后从大到小依次遍历所有因数,对于每个因数v[i],找到能整除v[i]的所有a[j],每找到一个数a[j],就去除v中不是a[j]因数的数。
麻烦是麻烦,好歹对了。

我的代码:
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<cstdio>
#include<cmath>
using namespace std;
const int N = 1010;
int t,n,a[N],b[N];
vector<int>v,v2;
int main() {
    
    
//	freopen("1.txt","r",stdin);
	cin>>t;
	while(t--) {
    
    
		cin>>n;
		for(int i=0; i<n; i++) cin>>a[i];
		sort(a,a+n);
		reverse(a,a+n);
		v.clear();
		for(int i=a[0]; i>=1; i--) {
    
    
			if(a[0]%i==0) v.push_back(i);
		}
		memset(b,0,sizeof b);
		b[0]=1;
		int sum=n-1,index=1,index2=0;
		cout<<a[0]<<" ";
		while(sum>0) {
    
    
			if(b[index]==1) {
    
    
				index++;
				if(index==n) {
    
    
					index=1;
					index2++;
					if(index2==v.size()) {
    
    
						for(int i=1; i<n; i++) {
    
    
							if(b[i]==0) cout<<a[i]<<" ";
						}
						sum=0;
						break;
					}
				}
				continue;
			}
			if(a[index]%v[index2]==0) {
    
    
				cout<<a[index]<<" ";
				b[index]=1;
				sum--;
				v2.clear();
				for(int i=index2;i<v.size();i++){
    
    
					if(a[index]%v[i]==0) v2.push_back(v[i]);
				}
				v=v2;
				index2=0;
			}
			index++;
			if(index==n) {
    
    
				index=1;
				index2++;
				if(index2==v.size()) {
    
    
					for(int i=1; i<n; i++) {
    
    
						if(b[i]==0) cout<<a[i]<<" ";
					}
					sum=0;
					break;
				}
			}
		}
		cout<<endl;
	}
	return 0;
}
总结:

这题错了两次,一次是因为没有考虑到细节,没有去更新v里的数。如果不更新v,找到的数虽然是最大值的因数v[i]的倍数,但v[i]可能不是其他数的因数。举个例子,a=[72,18,16,3],72的因数为[72,36,24,18,12,9,8,6,3,2,1],前两个数是72,18,选择第三个数的时候,按原本的逻辑就会选16,16是8的倍数,而3只是3的倍数,这样最后的答案就会是[72,18,16,3]。但是是错的,因为72,18,16的最大公约数是2,而72,18,3的最大公约数是3,所以应该先选3。这是因为我只考虑了最大值,忽略了其他已经被选走的数。所以在选中18之后,我们应该将v更新为[18,9,6,3,2,1],再进行后面的操作。第二次就是粗心,忘记v2.clear();

猜你喜欢

转载自blog.csdn.net/lmmmmmmmmmmmmmmm/article/details/108508983