题意:
一个数列中有n个数,正负均有;
每m个中至少取a个,而至多取b个;
求取出数总和减剩余数总和的最大值;
题解:
首先考虑取出数总和减剩余数总和的最大值就是两个取出的最大值减去所有总和all;
那么我们就可以dp出n个数取出的最大值ans;
然后2*ans-all就是答案;
m<=10的范围,于是可以状压;
设f[i][j]表示前i个数中的后m个数状态为j的取数的最大值;
于是预处理状态+dp扫一遍出解;
HINT:
由于有负数,所以要各种清负无穷,dp数组和ans都要清。。;
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 10001
using namespace std;
int f[N][2050],w[N],can[N];
int n,m,a,b,tot; //a->min,b->max
bool vis[N];
void dfs(int now,int deep,int cnt)
{
int i,j;
if(deep==m)
{
if(cnt<a) return ;
can[++tot]=now;
vis[now]=1;
return ;
}
if(cnt!=b)
dfs(now<<1|1,deep+1,cnt+1);
dfs(now<<1,deep+1,cnt);
}
int main()
{
int i,j,k,mod,all=0,ans=-0x7fffffff;
scanf("%d%d%d%d",&n,&m,&a,&b);
for(i=1;i<=n;i++)
{
scanf("%d",w+i);
all+=w[i];
}
for(i=1,mod=1;i<=m;i++)
{
mod<<=1;
}
dfs(0,0,0);
memset(f,-0x7f,sizeof(f));
for(i=1;i<=tot;i++)
{
f[0][can[i]]=0;
}
for(i=1;i<=n;i++)
{
for(j=1;j<=tot;j++)
{
k=can[j];
if(vis[(k<<1)%mod])
f[i][(k<<1)%mod]=max(f[i][(k<<1)%mod],f[i-1][k]);
if(vis[(k<<1|1)%mod])
f[i][(k<<1|1)%mod]=max(f[i][(k<<1|1)%mod],f[i-1][k]+w[i]);
}
}
for(i=1;i<=tot;i++)
{
ans=max(ans,f[n][can[i]]);
}
printf("%d",ans*2-all);
}