G

https://codeforces.com/problemset/problem/1077/F1

这个其实是一个比较简单的dp了

题目大意:

给你n个数,让你从n个数里选出x个数,并且每隔k个至少选一个数。

开始不知道怎么去写,也不知道怎么去定义dp

这个应该是对于dp不是特别的熟练,实际上dp用途很广,而且可以用到的地方很多,效果也很好。

这种时候就应该大胆一点,你需要什么,想达成什么效果那就去这样定义。

这个题目,我们希望dp可以帮我们解决前面n个数,选了x个数的最大和,而且这个还必须每隔k就选择了一个数。

这个是大问题,子问题是什么呢?

如果我们选第i个数,那么前面n-1个数,选了x-1个数的最大和。。。。

否则就是我们不选第i个数,那么前面n-1个数,选了x个数的最大和。  ----这种情况我们最多可以向前面推k-1次

所以我们要怎么定义呢?

首先要有一维来记录到第几个数了,还有一维就是要记录我们选了多少个了,

两维记录可以唯一确定每一个状态,所以两维就够了

那到底怎么去定义这个dp呢?dp的含义是什么呢?

这个我觉得不是很好想。

dp[i][j]表示前面i个数,已经选了j个数的最大和吗?

但是因为这个有区间限制,就是每隔k就必须有一个数,所以这个时候我们就要确定前面的那个值的位置,

所以我们就定义为已经选择了第i个数,前面i个数 选了j个数的最大和。

状态定义好了之后,就是转移方程的确定了。

dp[i][j]=max(dp[i][j],dp[s][j-1]+a[i])

这个s其实就是i往前推,然后去最大值,所以需要三维。

而且这个往前推最多推k个数。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <algorithm>
#include <vector>
#include <iostream>
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int maxn = 1010;
typedef long long ll;
ll dp[2 * maxn][2 * maxn];
ll a[maxn];

int main()
{
    int n, k, x;
    scanf("%d%d%d", &n, &k, &x);
    for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<=x;j++)
        {
            dp[i][j] = -inf;
        }
    }
    dp[0][0] = 0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=x;j++)
        {
            for(int h=1;h<=k&&h<=i;h++)
            {
                if (dp[i - h][j - 1] == -inf) continue;
                dp[i][j] = max(dp[i][j], dp[i - h][j - 1] + a[i]);
            //    printf("dp[%d][%d]=%lld
", i, j, dp[i][j]);
            }
        }
    }
    ll ans = -inf;
    for (int i = n - k + 1; i <= n; i++) ans = max(ans, dp[i][x]);
    if (ans < 0) printf("-1
");
    else printf("%lld
", ans);
    return 0;
}
线性dp
原文地址:https://www.cnblogs.com/EchoZQN/p/10945175.html