首页 > 其他分享 >P5858 「SWTR-03」Golden Sword DP+单调队列模板

P5858 「SWTR-03」Golden Sword DP+单调队列模板

时间:2023-01-26 21:22:27浏览次数:49  
标签:03 ch const Golden int ll SWTR dp define

P5858 「SWTR-03」Golden Sword - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

 

理解题意后,我们知道贪心和暴力枚举显然是不行的,联想到DP

我们设置dp[i][j]表示,第 i 种原料加入后,锅里一共有 j 个原料

再看题意:所幸的是,每放入一个原料之前,小 E 可以从中取出一些原料,数量不能超过  s  个。

所以在加入这种材料之前,锅里一共放如过 i-1 种原料,锅里一共有的原料范围  [  j-1, ,min(j+s-1,w)  ]

所以DP转移方程为:dp[i][j] = max( dp[i-1][k]+a[i]*j ) k∈ [  j-1, ,min(j+s-1,w)  ]

由于是有负数的可能,所以一开始我们把所有的dp原始初值令为-1e18,而dp[0][0]应该为0

45分Code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
#define pb push_back   //vector函数
#define popb pop_back  //vector函数
#define fi first
#define se second
const int N=5010;
//const int M=;
//const int inf=0x3f3f3f3f;     //一般为int赋最大值,不用于memset中
//const ll INF=0x3ffffffffffff; //一般为ll赋最大值,不用于memset中
int T,n,w,s;
ll a[N],dp[N][N];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
int main()
{
//    freopen("","r",stdin);
//    freopen("","w",stdout);
    n=read(),w=read(),s=read();
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++) dp[i][j]=-1e18;
    dp[0][0]=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=w;j++)
            for(int k=0;k<=s&&j+k-1<=w;k++) dp[i][j]=max(dp[i][j],dp[i-1][j+k-1]+a[i]*j);
    ll ans=-1e18;
    for(int i=1;i<=w;i++) ans=max(ans,dp[n][i]);
    printf("%lld",ans);
    return 0;
}

 

再来看DP转移方程:

DP转移方程为:dp[i][j] = max( dp[i-1][k]+a[i]*j ) k∈ [  j-1, ,min(j+s-1,w)  ]

dp[i][j]=max(dp[i-1][k])+a[i]*j; k∈ [  j-1, ,min(j+s-1,w)  ]

只要优化取 max(dp[i-1][k]) k∈ [  j-1, ,min(j+s-1,w)  ]即可。

对于这个,我们可以建一个单调队列用来维护 dp[i-1],而范围就是[  j-1, ,min(j+s-1,w)  ],这就是 移动窗口模板

简单讲一下单调队列:一般用于维护移动的下标[L,R]之间的最值。

用到的deque是用来存队列的下标的

 

 新建一个cnt, 用于表示选到了哪个数,一般来说cnt=0,这个cnt表示第一个没有取到的数;

如果cnt<=R,那么就把 cnt 加入队尾back,显然这还不够,还要保持单调性,(把队尾所有 小于等于 或 大于等于 cnt的全都pop_back() // 如果队列为空,直接加入)

那么队尾就操作好了。

而我们每次取数,取下标[L,R]中的最值,是从队头front取的。

显然队里的所有下标都是满足<=R的,所有我们只需让所有小于L的队头pop_front()就好了

单调队列的特点就是 : 先把小于<=R的数加入,再剔除<L的数,最后取队头

最终我们用队头进行DP转移

但是,无论何时何刻,我们都要保证要用队头时,队不是空的

此题单调队列的代码:

        deque<int> q; 
        int cnt=0;
        for(int j=1;j<=w;j++)
        {
            for(;cnt<=min(w,j+s-1);cnt++)  如果cnt<=R,那么就把 cnt 加入队尾back,显然这还不够,还要保持单调性,(把队尾所有 小于等于 或 大于等于 cnt的全都pop_back() // 如果队列为空,直接加
            {
                if(q.empty()) q.pb(cnt);
                else
                {
                    while(!q.empty()&&dp[i-1][q.back()]<=dp[i-1][cnt]) q.pop_back();
                    q.pb(cnt);
                }
            }
            while(!q.empty()&&q.front()<j-1 ) q.pop_front(); 显然队里的所有下标都是满足<=R的,所有我们只需让所有小于L的队头pop_front()就好
       if(!q.empty()) dp[i][j]=dp[i-1][q.front()]+a[i]*j;但是,无论何时何刻,我们都要保证要用队头时,队不是空的
        }

100分Code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
#define pb push_back   //vector函数
#define popb pop_back  //vector函数
#define fi first
#define se second
const int N=5010;
//const int M=;
//const int inf=0x3f3f3f3f;     //一般为int赋最大值,不用于memset中
//const ll INF=0x3ffffffffffff; //一般为ll赋最大值,不用于memset中
int T,n,w,s;
ll a[N],dp[N][N];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
int main()
{
//    freopen("","r",stdin);
//    freopen("","w",stdout);
    n=read(),w=read(),s=read();
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++) dp[i][j]=-1e18;
    dp[0][0]=0;
    for(int i=1;i<=n;i++)
    {
        deque<int> q; 
        int cnt=0;
        for(int j=1;j<=w;j++)
        {
            for(;cnt<=min(w,j+s-1);cnt++) 
            {
                if(q.empty()) q.pb(cnt);
                else
                {
                    while(!q.empty()&&dp[i-1][q.back()]<=dp[i-1][cnt]) q.pop_back();
                    q.pb(cnt);
                }
            }
            while(!q.empty()&&q.front()<j-1 ) q.pop_front();
            if(!q.empty()) dp[i][j]=dp[i-1][q.front()]+a[i]*j;
//            for(int k=j-1;k<=min(w,j+s-1);k++) dp[i][j]=max(dp[i][j],dp[i-1][k]+a[i]*j);
        }
    }
    ll ans=-1e18;
    for(int i=1;i<=w;i++) ans=max(ans,dp[n][i]);
    printf("%lld",ans);
    return 0;
}

 

最后放个55分用堆来写单调队列的DPCode:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
#define pb push_back   //vector函数
#define popb pop_back  //vector函数
#define fi first
#define se second
const int N=5010;
//const int M=;
//const int inf=0x3f3f3f3f;     //一般为int赋最大值,不用于memset中
//const ll INF=0x3ffffffffffff; //一般为ll赋最大值,不用于memset中
int T,n,w,s;
ll a[N],dp[N][N];
struct node
{
    int id;ll v;
    friend bool operator < (const node &a,const node &b)
    {
        return a.v<b.v;
    }
};
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
int main()
{
//    freopen("","r",stdin);
//    freopen("","w",stdout);
    n=read(),w=read(),s=read();
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++) dp[i][j]=-1e18;
    dp[0][0]=0;
    for(int i=1;i<=n;i++)
    {
        priority_queue<node> q; 
        int cnt=0;
        for(int j=1;j<=w;j++)
        {
            for(;cnt<=min(w,j+s-1);cnt++) q.push(node{cnt,dp[i-1][cnt]});
            while(!q.empty()&& q.top().id<j-1 ) q.pop();
            if(!q.empty()) dp[i][j]=q.top().v+a[i]*j;
//            for(int k=j-1;k<=min(w,j+s-1);k++) dp[i][j]=max(dp[i][j],dp[i-1][k]+a[i]*j);
        }
    }
    ll ans=-1e18;
    for(int i=1;i<=w;i++) ans=max(ans,dp[n][i]);
    printf("%lld",ans);
    return 0;
}

 

标签:03,ch,const,Golden,int,ll,SWTR,dp,define
From: https://www.cnblogs.com/Willette/p/17068201.html

相关文章

  • 03-你能不能自己写一个叫做java.lang.Object的类?
    前言:接着上一次https://www.cnblogs.com/webor2006/p/16609029.html的继续往下,距离上一篇已经过去快半年了,从我的博文记录中就可以清楚地看到:转眼2023年新春假期接近尾声......
  • C++ 和 Java 的区别(A C++ programmer's perspective)
    C++开发,周末看了2天Java教程,直接上手写Java。说下自己最明显的感受,用的不多,理解不一定对:【JVM】Java编译+解释,运行在JVM,跨平台移植方便,但执行速度/效率比C++低......
  • Day03 - 事务索引查询及PyMySQL
    1.将查询结果插入到另一张表中思考目前只有一个goods表,我们想要增加一个商品分类信息,比如:移动设备这个分类信息,只通过goods表无法完成商品分类的添加,那么如何实现添加......
  • TypeScript reuse object's props All In One
    TypeScriptreuseobject'spropsAllInOneexport{};constlog=console.log;//typeCSSProps={[prop:string]:CSSProps};//typeCSSProps=CSSProps[]......
  • Day - 03 JQuery及AJAX
    1.jquery介绍jQuery的定义jQuery是对JavaScript的封装,它是免费、开源的JavaScript函数库,jQuery极大地简化了JavaScript编程。jQuery的作用jQuery和JavaScript......
  • 解决:“error PRJ0003 : 生成“cmd.exe”时出错”
    昨晚用VS2005刚写好的程序,今天打算修改,不料在编绎时,却出现了错误,错误信息如下:“errorPRJ0003:生成“cmd.exe”时出错”解决方法:在VisualStudio2005中进行如下设置:打......
  • 问题:RuntimeError: Model class LuffyAPI.apps.user.models.UserInfo doesn't declare
    问题截图  报错原因提示app未注册,但实际上已经注册的#1.#settings配置文件移动后要将这个settings添加到环境变量中sys.path.insert(0,BASE_DIR)#将所有app......
  • 03初识MapReduce
    初识MapReduce一、什么是MapReduceMapReduce是一种编程范式,它借助Map将一个大任务分解成多个小任务,再借助Reduce归并Map的结果。MapReduce虽然原理很简单,但是使用MapRedu......
  • Windows Server 2003 SP2 中文版下载
       简体中文32位x86的版   在以下版本的Windows系统上安装:   WindowsServer2003Editions   WindowsServer2003R2Editions   WindowsStorage......
  • 递归(一)003:全排列
    003:全排列总时间限制:1000ms内存限制:65536kB描述给定一个由不同的小写字母组成的字符串,输出这个字符串的所有全排列。我们假设对于小写字母有'a'<'b'<...<......