近似乘積

近似乘積

題目鏈接: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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章