背包DP模型

4378. 选取数对 - AcWing题库

一开始想到是DP了,也想到状态表示了F[i][j]表示在前i个物品当中选,j个集合。

但是不知道怎么写状态转移方程,也没看出来这其实就是一个01背包模型。

于是就暴力写了个DFS骗了一半分,顺利的TLE了

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 10010;

int n, m, k;
LL a[N], sum[N], res;
string s = "1", t = "";

void dfs(int u, LL val)
{
    if(u >= k)
    {
        // cout << val << " ";
        res = max(res, val);
        return ;
    }
    
    if(s.find(t) == -1) return ;
    
    for(int i = 1; i <= n; i ++ )
    {
        string ss = s.substr(i);
        int pos = ss.find(t);
        if(pos != -1)
        {
            pos += i;
            // cout << "pos: " << pos << endl;
            for(int j = pos; j <= pos + m - 1; j ++ )   s[j] = '1';
            dfs(u + 1, val + sum[pos + m - 1] - sum[pos - 1]);
            
            for(int j = pos; j <= pos + m - 1; j ++ )   s[j] = '0';
        }
    }
    
}

int main()
{
    cin >> n >> m >> k;
    for(int i = 1; i <= n; i ++ )   cin >> a[i], sum[i] = sum[i - 1] + a[i];
    
    for(int i = 1; i <= n; i ++ )   s += "0";
    for(int i = 1; i <= m; i ++ )   t += "0";
    
    // cout << s << " " << t << endl;
    
    dfs(0, 0);

    cout << res << endl;
    

    
    return 0;
}

看了下别人的代码,这不就是一个01背包模型吗???

物品n对应背包的物品,集合个数k不就是背包的容量吗

难点在于集合中元素的个数m,不同于01背包,但其实也是很好处理的

我们每次只需要选一个集合就好了呀,把一个集合看做一个数,这样k个集合就是选k个数

至于从哪里开始选,我们规定,当前遍历到的物品i,要选的话,他就是该集合的最后一个元素,集合中的其余物品从它的前面选,这样我们就不需要考虑后面的元素了,

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 5010;

int n, m, k;
LL dp[N][N], a[N], s[N];

int main()
{
    cin >> n >> m >> k;
    for(int i = 1; i <= n; i ++ )   cin >> a[i], s[i] = s[i - 1] + a[i];
    
    for(int i = m; i <= n; i ++ )//从m开始,不然Segment Fault,数组下标<0
    {
        for(int j = 1; j <= k; j ++ )
        {
            dp[i][j] = dp[i - 1][j];    //不选
            dp[i][j] = max(dp[i][j], dp[i - m][j - 1] + (s[i] - s[i - m]));//选就选以当前节点i为最后一个元素的集合
        }
    }
    
    cout << dp[n][k] << endl;
    
    return 0;
}

大佬的优化代码 

# include <bits/stdc++.h>
# define mem(a,b) memset(a,b,sizeof(a))
# define lb(x) ((x) & -(x))
# define pi pair<int,int>
# define X first
# define Y second
# define ABS(x) ((x)>=0?(x):-(x))
# define debug freopen("r.txt","r",stdin)
using namespace std;
typedef long long ll;
ll f[5004];
ll a[5004],s[5004];
int n,m,k;
int main(){
    scanf("%d %d %d",&n,&m,&k);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++)
        s[i]=s[i-1]+a[i];
    for(int o=1;o<=k;o++){
        for(int j=1;j<=n;j++)
            f[j]=max(f[j],f[j-1]);
        for(int j=n;j>=m;j--)
            f[j]=max(f[j],f[j-m]+s[j]-s[j-m]);
    }
    printf("%lld\n",*max_element(f+1,f+n+1));
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值