2018年第九届蓝桥杯【C++省赛B组】【第十题:乘积最大】——贪心算法,附解题源码

蓝桥杯历届题目及解析汇总(附思路及代码)【点击此进入】


蓝桥杯,ACM算法学习【文档】【视频】大放送【点击此进入】

第十题

标题:乘积最大

给定N个整数A1, A2, … AN。请你从中选出K个数,使其乘积最大。

####请你求出最大的乘积,由于乘积可能超出整型范围,你只需输出乘积除以1000000009的余数。

####注意,如果X<0, 我们定义X除以1000000009的余数是负(-X)除以1000000009的余数。
####即:0-((0-x) % 1000000009)

【输入格式】
第一行包含两个整数N和K。  
以下N行每行一个整数Ai。  

对于40%的数据,1 <= K <= N <= 100  
对于60%的数据,1 <= K <= 1000  
对于100%的数据,1 <= K <= N <= 100000  -100000 <= Ai <= 100000  

【输出格式】
一个整数,表示答案。
【输入样例】
5 3 
-100000   
-10000   
2   
100000  
10000  

【输出样例】
999100009
再例如:
【输入样例】
5 3 
-100000   
-100000   
-2   
-100000  
-100000

【输出样例】
-999999829

解题思路:

先按照绝对值从大到小排序

###①假如选的k个数中必定有0,则结果为0
###②假如都是负数,此时若k为偶数,则选前k个数即可,若k为奇数,则只能从小的开始选k个数
###③假如都为正数,选前k个数即可
###④假如正负都有,此时,若前k个数有偶数个负数,则选前k个数即可

若前k个数有奇数个负数,则我们看看能不能从后面选一个大正数跟前面的小负数做交换,或者从后面选一个大负数跟前面的小正数做交换,二者取结果大的.

###此时分完所有情况若结果还为负数,我们看看输入是否有0,有零结果即为0,否则就只能输出这个负数了.

代码

#include
#define mem(a,b) memset(a,b,sizeof(a))
#define mod 1000000009
using namespace std;
typedef long long ll;
const int maxn = 1e6+5;
const double esp = 1e-7;
const int ff = 0x3f3f3f3f;
map::iterator it;

struct node
{
	ll x;
	int f;
}a[maxn];

int n,k;

bool cmp(node x,node y)
{
	return x.x> y.x;
}

ll solve(int o)
{
	ll ans = 1;
	int cnt = 0;
	if(o == 0)//从前往后乘 
	{
		for(int i = 1;i<= k;i++)
		{
			ans = (ans*a[i].x)%mod;
			if(a[i].f == 1)
				cnt++;
		}
	}
	else//从后往前乘 
	{
		for(int i = n;i> n-k;i--)
		{
			ans = (ans*a[i].x)%mod;
			if(a[i].f == 1)
				cnt++;
		}
	}
	
	if(cnt&1)
		return ans*(-1);
	return ans;
}

int main()
{
	cin>>n>>k;
	
	int flag = 0;
	int cnt = 0;
	for(int i = 1;i<= n;i++)
	{
		scanf("%lld",&a[i].x);
		if(a[i].x< 0)
		{
			a[i].f = 1;
			a[i].x = -a[i].x;
			cnt++;
		}
		else if(a[i].x> 0)
			a[i].f = 0;
		else
		{
			i--;n--;//我们不记录0,0只做迫不得已的选择 
			flag = 1;
		}
	}
	
	sort(a+1,a+n+1,cmp);
	
	ll ans = 0;
	
	if(n< k)//如果必须选0 
		ans = 0;
	else if(cnt == n)//如果都为负数 
	{
		if(k&1)
			ans = solve(1);
		else
			ans = solve(0);
	}
	else if(cnt == 0)//如果都为正数 
		ans = solve(0);
	else
	{
		int tmp = 0;
		for(int i = 1;i<= k;i++)
			if(a[i].f == 1)
				tmp++;
			
		if(tmp%2 == 0)//如果前k大的数有偶数个负数 
			ans = solve(0);
		else
		{
			ans = -1;//将其设置为负数 
			//尝试将前k个里面一个绝对值最小负数和后面最大正数交换 
			int p = -1,q = -1;
			for(int i = k+1;i<= n;i++)
				if(a[i].f == 0)
				{
					q = i;
					break;
				}
			for(int i = k;i>= 1;i--)
				if(a[i].f == 1)
				{
					p = i;
					break;
				}
			
			if(p!= -1&&q!= -1)
			{
				swap(a[p],a[q]);
				ans = solve(0);
				swap(a[p],a[q]);
			}
			
			//尝试将前k个里面一个最小正数和后面绝对值最大正数交换
			p = -1,q = -1;
			for(int i = k+1;i<= n;i++)
				if(a[i].f == 1)
				{
					q = i;
					break;
				}
			for(int i = k;i>= 1;i--)
				if(a[i].f == 0)
				{
					p = i;
					break;
				}
			
			if(p!= -1&&q!= -1)
			{
				swap(a[p],a[q]);
				ans = max(ans,solve(0));
				swap(a[p],a[q]);
			}
			
			//假如结果仍然小于0,我们只能尝试从最后往前乘了 
			if(ans< 0)
				ans = solve(1); 
		}
	}
	
	if(ans< 0)
		if(flag)//这时候0派上用场了 
		{
			cout<<0<

蓝桥杯,ACM算法进阶资料大放送

你可能感兴趣的:(【蓝桥杯题解】,【蓝桥杯题解】)