题目描述

解析
'这题挺好的
思路:dp[i][j]表示必须把i和j配对,可达到的最大值
首先:
dp[i][j]=dp[i-1][j-1]+a[i]*b[j];
然后可以分别尝试把男生或女生往前放弃一段:
for(int k=i-1;k>=1;k--){//男不要k到i-1
dp[i][j]=max(dp[i][j],dp[k-1][j-1]+a[i]*b[j]-(suma[i-1]-suma[k-1])*(suma[i-1]-suma[k-1]));
}
for(int k=j-1;k>=1;k--){//女不要k到j-1
dp[i][j]=max(dp[i][j],dp[i-1][k-1]+a[i]*b[j]-(sumb[j-1]-sumb[k-1])*(sumb[j-1]-sumb[k-1]));
}
这道题我看了题解
我本来也有类似的想法,但当时我觉得男生女生可能都放弃了一段,所以应该同时枚举男生和女生放弃的位置,这样时间复杂度就变成了n^4,无法接受
但是我忽略了一个性质;ab都不小于0,所以两边都放弃肯定不如让放弃的那两段再配对一些好,所以每次一定只会放弃一边
这样就变成了题解的n^3思路
另外本题还有一些有意思的细节:
1.再后面再加上一对水平为0的男女,最后统计dp[n+1][n+1],这样就可以包含那些没有取到第n对的正解
2.i不等于0时,dp[0][i]和dp[i][0]都是没有意义的,所以应该赋值为无穷小
3.虽然答案在int范围内,但前缀和之差平方的操作会爆int,所以应该开longlong
代码
#include<bits/stdc++.h>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=600;
const int M=2e6+100;
int m,n;
ll dp[N][N];
ll a[N],b[N],suma[N],sumb[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
suma[i]=suma[i-1]+a[i];
// printf("i=%d sum=%d\n",i,suma[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
sumb[i]=sumb[i-1]+b[i];
}
a[++n]=0;b[n]=0;
suma[n]=suma[n-1];sumb[n]=sumb[n-1];//再加一对
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++) dp[i][j]=-2e9;
}
dp[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
dp[i][j]=dp[i-1][j-1]+a[i]*b[j];
for(int k=i-1;k>=1;k--){//不要k到i-1
// printf("aa\n");
dp[i][j]=max(dp[i][j],dp[k-1][j-1]+a[i]*b[j]-(suma[i-1]-suma[k-1])*(suma[i-1]-suma[k-1]));
}
for(int k=j-1;k>=1;k--){//不要k到j-1
// printf("bb\n");
dp[i][j]=max(dp[i][j],dp[i-1][k-1]+a[i]*b[j]-(sumb[j-1]-sumb[k-1])*(sumb[j-1]-sumb[k-1]));
}
// printf("i=%d j=%d dp=%d\n",i,j,dp[i][j]);
}
}
printf("%lld",dp[n][n]);
return 0;
}

本文解析了如何使用动态规划解决情侣搭配问题,通过调整策略避免时间复杂度升至O(n^4),并介绍了关键细节如考虑全排列、初始化边界条件和处理溢出。代码展示了如何利用longlong类型存储大整数计算结果。
1053

被折叠的 条评论
为什么被折叠?



