不止代码:生日欢唱(ybtoj-区间dp)

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

题目描述

在这里插入图片描述

解析

'这题挺好的
思路: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;
}


thanks for reading!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值