双向队列 枚举模拟

双向队列 枚举模拟

原题链接

Problem Statement

有 N 块羊肉穿在同一根竹签上,从左到右美味值 依次为 V1, V2, …, VN。注意美味值可能为负数。

现在你想把一些羊肉夹到自己的盘子里。

你至多可以进行 K 次操作,可选择的操作如下:

操作 A:将羊肉串上最左端的羊肉夹到盘子中,必须保证羊肉串上有羊肉

操作 B:将羊肉串上最右端的羊肉夹到盘子中,必须保证羊肉串上有羊肉

操作 C:将盘子中的某块羊肉重新穿到羊肉串的最左端,必须保证盘子中有羊肉

操作 D:将盘子中的某块羊肉重新穿到羊肉串的最右端,必须保证盘子中有羊肉

找出夹到盘子中的羊肉的美味值之和的最大可能。

Constraints

所有输入的值都是整数
1 ≤ N ≤ 50
1 ≤ K ≤ 100
-107 ≤ Vi ≤ 107

Input

输入按以下格式由 标准输入 给出:

N K
V1 V2 … VN

Output

输出夹到盘子中的羊肉的美味值之和的最大可能。

Sample Input 1

6 4
-10 8 2 1 2 6

Sample Output 1

14
通过以下操作,你将会得到两块美味值分别为 8 和 6 的羊肉,总和 14 为最大值。

操作 A,把美味值为 -10 的羊肉夹到盘中
操作 B,把美味值为 6 的羊肉夹到盘中
操作 A,把美味值为 8 的羊肉夹到盘中
操作 D,把美味值为 -10 的羊肉穿回羊肉串的右端

Sample Input 2

6 4
-6 -100 50 -2 -5 -3

Sample Output 2

44

Sample Input 3

6 3
-6 -100 50 -2 -5 -3

Sample Output 3

0
什么都不做才是最优的。

分析

本题双端队列意为从数组的两端选择/插入元素,并求所得元素集合之和的最大值.

预处理:
输入元素可用数组存储,而对数组元素的双端操作可采用两个变量 i , j 前后访问;
而中间的元素集合可用数组存储;
根据N和Vn的数据范围可得int 不会越界,最后结果可定义一个最大值int变量.
枚举过程:

枚举的重点在于找出一般的状态.

本题有四种操作,取左端/取右端/放左端/放右端.
  
取数一定在放数之前,如果我们把相同的处理过程归类,
则过程可以简化为:先取数,后放数.
由于取数对次序要求严格,而放数不会影响最终结果,因此共有三种状态过程.
  1. 取左端

  2. 取右端

  3. 放数

    需要注意的是,负数会产生副作用.
    

我们在放数过程,只需要尽可能多(范围限制)得处理掉影响大的负数(排序).

假设从左端取l个数,右端r个数,在进行k次放数操作则有

int i,j;
for(i=0;i<l;i++)
	plate[i]=mutton[i];
for(j=0;j<r;j++)
	plate[i+j]=mutton[N-j-1];

sort(plate,plate+l+r);//依次处理掉影响大的元素

int k=0,temp=0;
while(k+l+r<K&&plate[k]<0)//至多进行K次操作,则放数次数k满足一定条件
	k++;
while(k<l+r)//求得处理后序列元素之和
{
	temp+=plate[k];
	k++;
}

一般状态找到了,再可对整体进行范围限制;

l,r之和必须小于等于元素总数N;l,r,k之和并且小于等于操作总数K;

l+r+k<=K,l+r<=N
对l和r的每种可能取值,都要进行一次上述过程,并且找出最大值.

int bound=min(N,K);
for(int l=0;l<=bound;l++)
{
	for(int r=0;r<=bound-l;r++)
	{
		/*上述状态处理
		int i,j;
		for(i=0;i<l;i++)
			plate[i]=mutton[i];
		for(j=0;j<r;j++)
			plate[i+j]=mutton[N-j-1];

		sort(plate,plate+l+r);

		int k=0,temp=0;
		while(k+l+r<K&&plate[k]<0)
			k++;
		while(k<l+r)
		{
			temp+=plate[k];
			k++;
		}
		*/
		res=max(res,temp);
	}
}
代码实现:
#include<iostream>
#include<algorithm>
using namespace std;

#define MAXN 51
int mutton[MAXN];
int plate[MAXN];

int main()
{
    int N,K,res=0;
    cin>>N>>K;
    
	for(int i=0;i<N;i++)
		cin>>mutton[i];
	
	int bound=min(N,K);
	for(int l=0;l<=bound;l++)
	{
		for(int r=0;r<=bound-l;r++)
		{
			int i,j;
			for(i=0;i<l;i++)
				plate[i]=mutton[i];
			for(j=0;j<r;j++)
				plate[i+j]=mutton[N-j-1];

			sort(plate,plate+l+r);

			int k=0,temp=0;
			while(k+l+r<K&&plate[k]<0)
				k++;
			while(k<l+r)
			{
				temp+=plate[k];
				k++;
			}
			
			res=max(res,temp);
		}
	}
	cout<<res<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值