Atcoder Beginner Contest 173 E

题意: 给定一个长度为 n n n的序列 a a a ∣ a i ∣ ≤ 1 0 9 |a_i|\leq10^9 ai109,从中选择 k ( k ≤ n ) k(k\leq n) k(kn)个数,使得答案最大,问最大答案是最少。
题解1:
模拟就完事了,考虑完所有情况, 1 A 1A 1A是不可能的,大概需要考虑的就是 k k k为奇数和偶数的情况吧。
代码:

#include
using namespace std;

typedef long long ll;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
ll pos[N], gp;
ll neg[N], gn;
int zero;
int n, k;

bool cmp(int a, int b) {
	return a > b;
}

int main()
{
	scanf("%d%d", &n, &k);
	for(int i = 1; i <= n; i++) {
		ll x; scanf("%lld", &x);
		if(x == 0) zero++;
		else if(x > 0) pos[++gp] = x;
		else neg[++gn] = x;
	}	
	
	sort(pos + 1, pos + 1 + gp, cmp);
	sort(neg + 1, neg + 1 + gn);
	
	ll res = 1;
	if(gp == n) {
		for(int i = 1; i <= k; i++) res = res * pos[i] % mod;
	} else if(gn == n) {
		if(k & 1) for(int i = n, j = 0; i >= 1 && j < k; i--, j++) res = res * neg[i] % mod;
		else for(int i = 1; i <= k; i++) res = res * neg[i] % mod;
	} else if(zero == n) {
		res = 0;
	} else {
		//正数和负数够了,不够k那么必然是0
 		if(gp + gn >= k) {
 			//如果k是奇数,那么存在正数的情况就是gp>=1, 然后剩余gn个负数和gp-1个正数可以凑出来k-1
 			if(k & 1) {
 				if(gp >= 1) {		
 					//判断是否可以得到正数答案 
 					if(gp + gn / 2 * 2 >= k) {
 						res = res * pos[1] % mod;
 						int i = 2, j = 1, cnt = k - 1;
 						while(i + 1 <= gp && j + 1 <= gn && cnt) {
 						if(pos[i] * pos[i + 1] >= neg[j] * neg[j + 1]) {
 							res = res * pos[i] % mod * pos[i + 1] % mod;
 							i += 2, cnt -= 2;
 						}
 						else {
 							res = res * neg[j] % mod * neg[j + 1] % mod;
 							j += 2, cnt -= 2;	
						}
 						}
	 					while(cnt && i + 1 <= gp) {
	 						res = res * pos[i] % mod * pos[i + 1] % mod;
	 						i += 2, cnt -= 2;
	 					}
	 					while(cnt && j + 1 <= gn) {
	 						res = res * neg[j] % mod * neg[j + 1] % mod;
	 						j += 2, cnt -= 2;
	 					}
 					}
 					
 					//gp+gn>=k,gp+gn/2*2
					else {
						for(int i = 1; i <= gp; i++) res = res * pos[i] % mod;
						for(int i = 1; i <= gn; i++) res = res * neg[i] % mod;
					}
 				}
 				//无pos,那么就先将所有负数都算上,最后统一处理负数
 				else if(gp < 1){
 					int j = 0;
 					for(int i = gn; i >= 1 && j < k; i--, j++) res = res * neg[i] % mod;
 					if(j < k) res = 0;
 				}
 			}
 			
 			//k为偶,和上述的k&1后将k-1的情况差不多
 			else {
 				int cnt = gp / 2 * 2 + gn / 2 * 2;
 				if(cnt >= k) {
 					cnt = k;
 					int i = 1, j = 1;
 					while(i + 1 <= gp && j + 1 <= gn && cnt) {
 						if(pos[i] * pos[i + 1] >= neg[j] * neg[j + 1]) {
 							res = res * pos[i] % mod * pos[i + 1] % mod;
 							i += 2, cnt -= 2;
 						}
 						else {
 							res = res * neg[j] % mod * neg[j + 1] % mod;
 							j += 2, cnt -= 2;	
						}
 					}
 					while(cnt && i + 1 <= gp) {
 						res = res * pos[i] % mod * pos[i + 1] % mod;
 						i += 2, cnt -= 2;
 					}
 					while(cnt && j + 1 <= gn) {
 						res = res * neg[j] % mod * neg[j + 1] % mod;
 						j += 2, cnt -= 2;
 					}
				 }
 				//由于k是偶数,所以若sur=k,必然是sur=2
 				else {
 					int sur = (gp & 1) + (gn & 1);
 					if(cnt + sur >= k) {
 						if(zero > 0) res = 0;
 						else {
 							for(int i = 1; i <= gn; i++) res = res * neg[i] % mod;
 							for(int i = 1; i <= gp; i++) res = res * pos[i] % mod;
 						}
 					} 
 					
 					else res = 0;
 				}
 			}
 		}
 		
 		else res = 0;
	}
	//统一解决下,若答案<0但是zero>0,那么答案为0更大
	if(res < 0 && zero > 0) res = 0;
	else res = (res % mod + mod) % mod;
	
	printf("%lld\n", res);
	
	return 0;
} 

题解2:参考了 q l s qls qls的思路:传送门

我们的目标是尽可能让答案为非负数。所以考虑从序列 q q q中选择负数的情况必然是选择了一正一负,此情况只有序列 q q q中不存在 0 0 0才会出现。

情况 1 1 1 n = k n=k n=k,所有数都得选
情况 2 2 2 m i n ( q i ) < 0 min(q_i)<0 min(qi)<0,若 k k k是偶数,那么选绝对值更大的 k k k个,否则选绝对值更小的 k k k
情况 3 3 3:对 q q q排序后,显然 q n ≥ 0 q_n\geq0 qn0
( 1 ) (1) (1)考虑 k k k为偶数,那么就从两边取 m a x ( q [ l ] × q [ l + 1 ] , q [ r − 1 ] × q [ r ] ) max(q[l]\times q[l+1],q[r-1]\times q[r]) max(q[l]×q[l+1],q[r1]×q[r])
此时若取到负数,必然是 k = n k=n k=n时,因为取负数时必然剩一奇一偶,而 k = n k=n k=n为情况 1 1 1所讨论的,故答案必然大于等于 0 0 0

( 2 ) (2) (2)考虑 k k k为奇数,若 q n q_n qn等于 0 0 0,那么答案就是 0 0 0,因为全取负数答案就是负数。
q n > 0 q_n>0 qn>0,那么也取 q n q_n qn,此时 k < n kk<n,可以保证答案不为负数,所以要尽可能选绝对值大的,但是选择了负数会直接将整个值变为负,故选择正数中的最大的即 q n q_n qn
证明如下:
取了 q n q_n qn后还得取 k − 1 k-1 k1个数, k − 1 k-1 k1是偶数,此时相当于情况 ( 1 ) (1) (1),只不过初始值为 q n > 0 q_n>0 qn>0,可选数数量为 n − 1 n-1 n1,要选数数量为 k − 1 k-1 k1。由于 ( 1 ) (1) (1)的结论,故答案也必然是大于等于 0 0 0的。
代码:

#include
using namespace std;

typedef long long ll;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
ll q[N]; 
int n, k;

int main()
{
	scanf("%d%d", &n, &k);
	for(int i = 1; i <= n; i++) scanf("%lld", &q[i]);
	
	sort(q + 1, q + 1 + n);
	
	ll res = 1;
	if(n == k || q[n] < 0) {
		//奇数就选绝对值尽量小,偶数就选绝对值尽量大 
		if(k & 1) for(int i = n - k + 1; i <= n; i++) res = res * q[i] % mod;
		else for(int i = 1; i <= k; i++) res = res * q[i] % mod;
	}
	else {
		int l = 1, r = n;
		//目标是为了使得最后的答案尽可能为非负数 
		//考虑无论如何k为奇数时都选择q[n],此时q[n]>=0,
		//若q[n]为0那么答案就是0,若q[n]>0,则选择后正数数量减1,然后继续k为偶数,从[1~r]中选择 
		if(k & 1) res = res * q[r] % mod, r--, k--;
		
		//考虑整个序列中出现q[x]*q[x+1]为负数必然是不存在0的时候 
		//如果选择了乘积为负数必然是只剩两个元素了 
		while(k) {
			if(q[l] * q[l + 1] > q[r] * q[r - 1]) res = res * q[l] % mod * q[l + 1] % mod, l += 2;
			else  res = res * q[r] % mod * q[r - 1] % mod, r -= 2;
			k -= 2;
		}
	}
	
	res = (res % mod + mod) % mod;
	printf("%lld\n", res);
	return 0;
}

你可能感兴趣的:(Atcoder)