近似乘积

近似乘积

题目链接:jzoj 3925jzoj\ 3925

题目

给你一个集合AA和一个正整数nn。要求找到三个不在集合AA中的正整数xx , yy , zz,使得其乘积尽量接近nn,即最小化 n  xyz|n\ –\ x * y * z|xx , yy , zz可以相同。
若有多组解,则最小化xx;若仍有多组解,则最小化yy;若仍有多组解,最小化zz

输入

第一行,一个整数TT,表示测试数据组数。
每组数据包含两行。第一行开头的整数mm,表示集合AA有多少个数,随后mm个整数,依次表示集合AA的每个元素。第二行为一个整数nn

输出

输出TT行,每行三个整数,依次表示每组数据的答案xx , yy, zz

样例输入

3
2 2 4
4
1 1
7
2 1 15
90

样例输出

1 1 3
2 2 2
2 5 9

数据范围

40%40\% 的数据:1 ⁣<= ⁣m ⁣<= ⁣101 ⁣<= ⁣n ⁣<=100.1\! <=\! m\! <=\! 10,1\! <=\! n\! <= 100.
70%70\% 的数据:1 ⁣<= ⁣m,n ⁣<= ⁣1000.1\! <=\! m, n\! <=\! 1000.
100%100\% 的数据:1 ⁣<= ⁣m,n,Ai<= ⁣1061 ⁣<= ⁣T ⁣<= ⁣4.1\! <=\! m, n, A_i <=\! 10^6,1\! <=\! T\! <=\! 4.

思路

这道题是一个要有优化的模拟。

我们让这三个数x ⁣ ⁣  ⁣  ⁣ ⁣<=  ⁣  ⁣ ⁣ ⁣ ⁣y ⁣ ⁣<= ⁣ ⁣zx\!\!\ \!\ \!\!<=\ \!\ \!\!\!\!y\!\!<=\!\!z,那我们就一个一个枚举它们,并且去掉那些不能用的数字。
但是还是会超时,那我们就要继续优化:
我们要让它们的乘积尽可能接近nn,那我们设ansans为最小的答案(即与nn相差的最小值),那我们就可以让它们的乘积小于n ⁣+ ⁣ansn\!+\!ans的时候才看是否要和当前的最优解(ansans)交换。因为这样,的出来的值要么小于nn,要么大于nn而且与nn的误差小于等于ansans,就不会漏掉其它更优的解。

但是还有一个问题,就是当我们只枚举了xx或者只枚举了xxyy的时候,又应该怎么判断呢?
很简单,因为我们上面让x ⁣ ⁣<= ⁣ ⁣y ⁣ ⁣<= ⁣ ⁣zx\!\!<=\!\!y\!\!<=\!\!z,那我们枚举xx的时候就让xxx<=n+ansx*x*x<=n+ans,枚举yy的时候就让xyy<=n+ansx*y*y<=n+ans,就可以了。
(枚举zz的时候就让xyz<=n+ansx*y*z<=n+ans,这个就不用说了吧)

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

int T, m, n, x, ans, ansi, ansj, ansk, a[1000001];
bool in[10000001];

int main() {
	scanf("%d", &T);//读入
	
	for (int i = 1; i <= T; i++) {
		ans = 1000000000;//初始化
		memset(a, 0, sizeof(a));//
		memset(in, 0, sizeof(in));
		
		scanf("%d", &m);//读入
		for (int i = 1; i <= m; i++) {
			scanf("%d", &x);//读入
			in[x] = 1;//记录
		}
		for (int i = 1; i <= 1000001; i++)
			if (!in[i])
				a[++a[0]] = i;//可以用
		scanf("%d", &n);//读入
		
		if (a[1] > n) {//用最小的都还是比要求的值大
			printf("%d %d %d\n", a[1], a[1], a[1]);//只能让三个数都最小
			continue;
		}
		for (int i = 1; a[i] * a[i] * a[i] <= (n + ans) && i <= a[0]; i++)//枚举第一个数
			for (int j = i; a[i] * a[j] * a[j] <= (n + ans) && j <= a[0]; j++)//枚举第二个数
				for (int k = j; a[i] * a[j] * a[k] <= (n + ans) && k <= a[0]; k++)//枚举第三个数
					if (ans > abs(n - a[i] * a[j] * a[k])) {//更小
						ans = abs(n - a[i] * a[j] * a[k]);//更换
						ansi = a[i];
						ansj = a[j];
						ansk = a[k];
					}
		
		printf("%d %d %d\n", ansi, ansj, ansk);//输出
	}
	
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章