dp训练计划——poj3186区间dp简单题(递推+记忆化搜索)

题目链接:https://vjudge.net/problem/POJ-3186

题目大意:

给你n个数字v(1),v(2),...,v(n-1),v(n),每次你可以取出最左端的数字或者取出最右端的数字,一共取n次取完。假设你第i次取的数字是x,你可以获得i*x的价值。你需要规划取数顺序,使获得的总价值之和最大。

Input

第一行一个数字n(1<=n<=2000)。
下面n行每行一个数字v(i)。(1<=v(i)<=1000)

Output

输出一个数字,表示最大总价值和。

Sample Input

5
1
3
1
5
2

Sample Output

43

题解:

一道区间dp的简单题,顺便重新看了一下以前我自己写的区间dp的博客,区间dp入门

状态dp[i][j]表示从i到j的最大价值总和。

状态转移方程dp[i][j]=max(dp[i+1][j]+a[i]*(n+i-j),dp[i][j-1]+a[j]*(n+i-j)).

这里n+i-j就表示第几次取,因为j-(i+1)+1就表示从i+1到j区间的数的个数,即还有这么j-i个数没取,那么取a[i]就是第n-(j-i)次,i到j-1区间也一样。

代码实现:

#pragma GCC optimize(2)
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#define PI atan(1.0) * 4
#define E 2.718281828
#define rp(i, s, t) for (register int i = (s); i <= (t); i++)
#define RP(i, t, s) for (register int i = (t); i >= (s); i--)
#define ll long long
#define ull unsigned long long
#define mst(a, b) memset(a, b, sizeof(a))
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define pii pair<int, int>
#define mp make_pair
#define pb push_back
#define debug printf("ac\n");
using namespace std;
inline int read()
{
    int a = 0, b = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-')
            b = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        a = (a << 3) + (a << 1) + c - '0';
        c = getchar();
    }
    return a * b;
}
const int INF = 0x3f3f3f3f;
const int N = 2e3 + 7;
int dp[N][N], a[N];
int n;
int main()
{
    mst(dp,0);
    n = read();
    rp(i, 1, n) a[i] = read();
    for (int len = 1; len <= n; len++){ //枚举区间长度
        for (int i = 1; i < n; ++i){//枚举区间的起点
            int j = i + len - 1; //根据起点和长度得出终点
            if (j>n)break; //符合条件的终点
            dp[i][j]=max(dp[i+1][j]+a[i]*(n+i-j),dp[i][j-1]+a[j]*(n+i-j));
        }
    }
    printf("%d\n", dp[1][n]);
    return 0;
}

还有一种记忆化搜索的实现,思路更清晰,而且不用再算第几次取,只需要在搜索的时候记录就行。

状态和递推的状态是一样的。

代码实现:

#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
#define PI atan(1.0)*4
#define E 2.718281828
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define debug printf("ac\n");
using namespace std;
inline int read()
{
    int a=0,b=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')
            b=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        a=(a<<3)+(a<<1)+c-'0';
        c=getchar();
    }
    return a*b;
}
const int INF = 0x3f3f3f3f;
const int N=2e3+7;
int dp[N][N],a[N];
int n;
int dfs(int l,int r,int times){
    if(dp[l][r]>=0) return dp[l][r];
    if(l>r||l<=0||r>=n+1) return 0;
    return dp[l][r]=max(dfs(l,r-1,times+1)+a[r]*times,dfs(l+1,r,times+1)+a[l]*times);
}
int main(){
    n=read();
    rp(i,1,n) a[i]=read();
    mst(dp,-1);
    dfs(1,n,1);
    printf("%d\n",dp[1][n]);
    return 0;
}
发布了342 篇原创文章 · 获赞 220 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_43472263/article/details/104687357
今日推荐