题意
给你两个序列,你可以从序列二中取出一些数插在序列二的任何位置。
此后,你可以从新的序列中取出一些不相邻的数,问你取出的数的和的最大值是多少。
思路
一眼动态规划。
假设第一个序列长为 n n n,第二个序列长为 m m m。
定义: d p i , j , k , x , y dp_{i,j,k,x,y} dpi,j,k,x,y 表示目前枚举到第 i i i 个数,有 j j j 个相邻的数要选,有 k k k 个相邻的数两个都不选时的和的最大值。其中 x x x 表示目前这个数选不选( 0 0 0 表示不选, 1 1 1 表示选), y y y 表示整个序列的第一个数选不选 ( 0 0 0 表示不选, 1 1 1 表示选)。
为了保证有解,我们要使 j j j 的值必须小于等于 m m m,而 k k k 的值可以大于 m m m,因为相邻两个数不选的情况也是不相邻。
一旦 k k k 的值等于 m + 1 m+1 m+1 后,我们就不用管 k k k 比 m + 1 m+1 m+1 更大的情况了,因为此时 k k k 的值对最终答案没有影响。
我们可以先取出序列一中的数,找出每一个情况的和的最大值,并且要拿出 j j j 个序列二中的数来当中间的数,因为对答案没有影响,又要使情况最优,所以要优先拿小的数。
接下来,我们可以把序列二剩下的数优先填入两个都不选的中间位置,也就是 k k k 个位置,因为对答案有影响,又要使情况最优,所以要优先拿大的数。
设剩下的序列二中的数有 s s s 个,这 s s s 个数可以插到开头或结尾的位置。
如果开头的数选,结尾的数不选,则应该把剩下的数插到结尾位置,对答案有贡献的数有 s 2 \frac {s}{2} 2s(向上取整)个数。
如果开头的数不选,结尾的数选,则应该把剩下的数插到开头位置,对答案有贡献的数有 s 2 \frac {s}{2} 2s(向上取整)个数。
如果开头的数选,结尾的数也选,则应该把剩下的数插到开头或结尾的任意位置,对答案有贡献的数有 s 2 \frac {s}{2} 2s(向下取整)个数。
如果开头的数不选,结尾的数也不选,则应该把剩下的数平均分配到开头和结尾的位置,对答案有贡献的数有 s 2 + 1 \frac {s}{2}+1 2s+1(向下取整)个数。
为了使答案最优,我们要在剩下的序列二的数中取尽量大的数,最后再加上满足条件的 dp 数组中的情况就是答案了。
AC 代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,a[3005],b[105],zans=0,dp[2][105][105][2][2];//全部数量,00个数,11个数,目前的01,第一个数的01
bool cmp(int x,int y)
{
return x>y;
}
int qh(int i,int j,int x,int y)
{
int o=m-j,sum=0;
for(int s=1;s<=min(o,i);s++)
{
sum+=b[s];
}
if(o<=i)
{
return sum;
}
int p=m-min(o,i)-j;
int f=p+(!x)+(!y);
for(int s=min(o,i)+1;s<=min(o,i)+f/2;s++)
{
sum+=b[s];
}
return sum;
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
scanf("%lld",&m);
for(int i=1;i<=m;i++)
{
scanf("%lld",&b[i]);
}
sort(b+1,b+m+1,cmp);
for(int i=1;i<=n;i++)
{
memset(dp[i%2],128,sizeof(dp[i%2]));
if(i==1)
{
dp[1][0][0][0][0]=0;
dp[1][0][0][1][1]=a[1];
continue;
}
for(int j=0;j<=m+1;j++)
{
for(int k=0;k<=m;k++)
{
if(j==0)
{
dp[i%2][j][k][0][0]=dp[(i-1)%2][j][k][1][0];
dp[i%2][j][k][0][1]=dp[(i-1)%2][j][k][1][1];
}
else
{
dp[i%2][j][k][0][0]=max(dp[(i-1)%2][j-1][k][0][0],dp[(i-1)%2][j][k][1][0]);
dp[i%2][j][k][0][1]=max(dp[(i-1)%2][j-1][k][0][1],dp[(i-1)%2][j][k][1][1]);
}
if(j==m+1)
{
dp[i%2][j][k][0][0]=max(dp[(i-1)%2][j][k][0][0],dp[i%2][j][k][0][0]);
dp[i%2][j][k][0][1]=max(dp[(i-1)%2][j][k][0][1],dp[i%2][j][k][0][1]);
}
if(k==0)
{
dp[i%2][j][k][1][0]=dp[(i-1)%2][j][k][0][0]+a[i];
dp[i%2][j][k][1][1]=dp[(i-1)%2][j][k][0][1]+a[i];
}
else
{
dp[i%2][j][k][1][0]=max(dp[(i-1)%2][j][k][0][0],dp[(i-1)%2][j][k-1][1][0])+a[i];
dp[i%2][j][k][1][1]=max(dp[(i-1)%2][j][k][0][1],dp[(i-1)%2][j][k-1][1][1])+a[i];
}
}
}
}
for(int i=0;i<=m+1;i++)
{
for(int j=0;j<=m;j++)
{
for(int x=0;x<=1;x++)
{
for(int y=0;y<=1;y++)
{
if(dp[n%2][i][j][x][y]>0)
{
int ans=dp[n%2][i][j][x][y];
zans=max(zans,ans+qh(i,j,x,y));
}
}
}
}
}
cout<<zans;
return 0;
}