【2017年浙江工业大学大学生程序设计迎新赛决赛】G 取数游戏二【DP or 记忆化DFS】

本文介绍了一种通过动态规划算法解决最大价值取数问题的方法,包括递推公式和记忆化搜索实现,旨在最大化从两端取数所获得的价值总和。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
给定两个长度为n的整数列A和B,每次你可以从A数列的左端或右端取走一个数。假设第i次取走的数为ax,则第i次取走的数的价值vi=bi⋅ax,现在希望你求出∑vi的最大值。
输入描述:
第一行一个数T,表示有T组数据。
对于每组数据,第一行一个整数n,
接下来两行分别给出A数列与B数列。
输出描述:
每一组数据输出一行,最大的∑vi。
示例1
输入

2
2
1 1000
2 1
5
1 3 5 2 4
1 2 3 4 5
输出

2001
52
说明

对于第二个样例,
第一次从左边取走a1,v1=a1⋅b1=1,
第二次从左边取走a2,v2=a2⋅b2=6,
第三次从右边取走a5,v3=a5⋅b3=12,
第四次从右边取走a4,v4=a4⋅b4=8,
第五次取走剩下的a3,v5=a3⋅b5=25。
总价值∑vi=1+6+12+8+25=52
备注:
T≤10
1≤n≤103
1≤ai,bi≤103

分析: 取数的过程可以为:
0> 左边取零个,其余都取右边的
1 >左边取一个,其余都取右边的
2> 左边取两个,其余都取右边的
3 >左边取三个,其余都取右边的

..
根据这个规律,我们可以用DP来解决,用dp [ i ] [ j ] 来表明 左边取i个,右边取j个能够获得的最大值。
怎么找状态转移方程呢? 我们可以思考一下,对于dp [ i ] [ j ] 这个状态,是能够由那些状态得到的。就可以得到:
状态转移方程 :
dp[ i ] [ j ] = max(dp[ i - 1][ j ] + a[ i ] * b[ j+i ] , dp[ i ][ j -1 ] + a[ n - j +1 ] *b [ j + i ] ) .
根据这个状态转移方程我们可以得到 递推遍历的方向,首先看外层循环,对于dp[ i ] 层来说 是需要dp[ i- 1 ]层来递推,所以我们可知外层循环正向遍历i。同理内层循环 也是要正向遍历j。
代码

#include<bits/stdc++.h>
using namespace std;
#define LL long long

const int MAXN = 1e3+11;
const int MAXM = 1e6 ;
const LL mod = 1e9+7 ;

int a[MAXN],b[MAXN];
int dp[MAXN][MAXN];
int main(){
    int T ;cin>>T;
    while(T--){
            int n;cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int j=1;j<=n;j++) cin>>b[j];
        for(int i=1;i<=n;i++) { // 一直紧着一边拿可得到的最大值
            dp[i][0]=dp[i-1][0]+a[i]*b[i];
            dp[0][i]=dp[0][i-1]+a[n-i+1]*b[i];
        }
        int ans=0;
        for(int i=1;i<=n;i++){// 外层正向遍历
            for(int j=1;j<=n;j++) { // 内层也要正向遍历
                dp[i][j]=max(dp[i-1][j]+a[i]*b[i+j],dp[i][j-1]+a[n-j+1]*b[j+i]) ;
                if(i+j==n) ans=max(ans,dp[i][j]);
            }
        }
        cout<<ans<<endl;
     }
return 0;
}

记忆化DFS

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define CLOSE() ios_base::sync_with_stdio(false)

const int MAXN = 1e3+11;
const int MAXM = 1e6 ;
const LL mod = 1e9+7 ;

int a[MAXN],b[MAXN];
int dp[MAXN][MAXN];

int  dfs(int l,int r,int step){
    if(l>r) return 0;
    if(dp[l][r]) return dp[l][r];
    int temp=0;
    temp=max(temp,dfs(l+1,r,step+1)+b[step]*a[l]);
    temp=max(temp,dfs(l,r-1,step+1)+b[step]*a[r]);
    return dp[l][r]=temp;
}

int main(){
    CLOSE();
    int T; cin>>T;
    while(T--){
        memset(dp,0,sizeof(dp));
        int n;cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<=n;i++) cin>>b[i];
        dfs(1,n,1);
        cout<<dp[1][n]<<endl;
    }
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值