一本通OJ-生日欢唱
生日欢唱
题意
n个男,n个女排成两列。可以选择上来唱歌获得 $ a[i]*b[j]$ 的价值,否则若男 \(or\) 女连续不上来损失 \((\sum a[i]) ^ 2\)的价值。可以上来也可以不上来。求最大价值。
分析
显然是区间dp,考虑\(f[i][j]\)表示考虑前\(i\)个男生,前\(j\)个女生并且强制让\(i,j\)对唱最大价值。有转移如下。
不难考虑可以使一部分男生/女生不上来。则有 \(f[i][j]=max(f[k][j-1]+a[i]*b[j]-\sum a[s])\)
同理有女生。
最后强制令\(n+1\)价值都为0,强制匹配,答案为\(f[n+1][n+1]\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
const int N=310;
int a[N],suma[N],b[N],sumb[N];
int f[N][N];
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
suma[i]=suma[i-1]+a[i];
}
for(int i=1;i<=n;i++){
scanf("%lld",&b[i]);
sumb[i]=sumb[i-1]+b[i];
}
suma[n+1]=suma[n],sumb[n+1]=sumb[n];
for(int i=1;i<=n;i++){
f[1][i]=a[1]*b[i]-(sumb[i-1]-sumb[0])*(sumb[i-1]-sumb[0]);
f[i][1]=a[i]*b[1]-(suma[i-1]-suma[0])*(suma[i-1]-suma[0]);
}
for(int i=2;i<=n+1;i++){
for(int j=2;j<=n+1;j++){
for(int k=1;k<i;k++){
f[i][j]=max(f[i][j],f[k][j-1]+a[i]*b[j]-(suma[i-1]-suma[k])*(suma[i-1]-suma[k]));
}
for(int k=1;k<j;k++){
f[i][j]=max(f[i][j],f[i-1][k]+a[i]*b[j]-(sumb[j-1]-sumb[k])*(sumb[j-1]-sumb[k]));
}
}
}
printf("%lld",f[n+1][n+1]);
return 0;
}