当前位置: 首页 > article >正文

11.22糖丸比赛题解

为什么这么多dp!!!

Bottles

1.一道背包问题(还涉及贪心)

2.考虑转移方程,在这道题中,关键变量只有两个 a , b 。第一问很好求,根据贪心,先对瓶子的容量进行降序排序,求前若干个瓶子使他们的容量大于等于所有水的体积时的个数即可。

在选择瓶子最少的情况下,方案是不一定的。通过观察得知, 第二问就是要在第一问的个数确定时,求出选择瓶子的水最多的方案 。选择了若干个瓶子,就意味着其他的瓶子的水要倒入其中,花费就为他们水的体积之和 。然后,考虑转移方程。设 fi​ 表示当水的体积是 i 时所需要的最少的瓶子数。

显然,根据一般的背包问题,有 fi​=maxf i−bj​​ +1 。

同时,根据上面的分析,我们要求的是此时水体积最大的,所以附加上一个 ans 数组, ansi​ 表示 ansi​ 表示当容积为 i 时水体积的最大值 。

#include<bits/stdc++.h>
using namespace std;
const int N=110;
struct node{
	int a,b;
}c[N];
int n,ans,suma,sumb,k=0,f[N*N][N];
bool cmp(node x,node y){
	return x.b>y.b;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>c[i].a;
		suma+=c[i].a;
	}
	for(int i=1;i<=n;i++) cin>>c[i].b;
	sort(c+1,c+n+1,cmp);
	while(sumb<suma) sumb+=c[++k].b;
	cout<<k<<" ";
	memset(f,128,sizeof(f));
	f[0][0]=0;
	//cout<<f[1][1]<<endl;
	for(int i=1;i<=n;i++)
		for(int j=sumb;j>=c[i].b;j--)
			for(int kk=1;kk<=k;kk++){
				f[j][kk]=max(f[j][kk],f[j-c[i].b][kk-1]+c[i].a);
			}
	for(int i=suma;i<=sumb;i++) ans=max(ans,f[i][k]);
	cout<<suma-ans;
	return 0;
}

上课

1.贪心题

2.拆方差的式子,方差等于平方的平均减平均的平方。因为总和固定,所以平均的平方固定,所以最小化平方和即可。假设当前对于 x 已求出最小方差的序列,要求 x+1 的最小方差。因为要最小化平方和,所以要把最小的能加的位置加一。原因是平方和公式增加的量是两倍的原数,所以要原数尽可能小。把初始状态全部取 li​,然后考虑增量即可。每次把 x 加上一太慢了,考虑对区间按位处理。记录当前位置有多少个区间可以用来加一。

方差式子整理之后就是:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e6+10,mx=1e6,mod=998244353;
struct query{
    ll x,id,ans;
    friend bool operator<(query xy,query zb){
        return xy.x<zb.x;
    }
}qq[N];
ll n,q,invn,l[N],r[N],cnt[N],ans,ansx;
inline ll p(ll x,ll y){
    if(!y) return 1;
    if(y&1) return p(x,y^1)*x%mod;
    return p(x*x%mod,y>>1);
}
int main(){
    scanf("%lld%lld",&n,&q);
    invn=p(n,mod-2);
    for(ll i=1;i<=n;i++){
        scanf("%lld%lld",&l[i],&r[i]);
        ans+=l[i]*l[i];
        ansx+=l[i];
        cnt[l[i]+1]++;
        cnt[r[i]+1]--;
    }
    for(ll i=0;i<=mx;i++) cnt[i]+=cnt[i-1];
    for(ll i=1;i<=q;i++){
        scanf("%lld",&qq[i].x);
        qq[i].id=i;
    }
    sort(qq+1,qq+q+1);
    for(ll i=1,j=0;i<=q;i++){
        while(ansx<qq[i].x){
            if(qq[i].x-ansx<cnt[j]){
                cnt[j]-=(qq[i].x-ansx);
                (ans+=(qq[i].x-ansx)*(j*2-1)%mod)%=mod;
                ansx=qq[i].x;
                break;
            }
            ansx+=cnt[j];
            (ans+=cnt[j]*(j*2-1)%mod)%=mod;
            j++;
        }
        qq[i].ans=ans;
        qq[i].x%=mod;
    }
    sort(qq+1,qq+q+1,[](query xy,query zb){
        return xy.id<zb.id;
    });
    for(ll i=1;i<=q;i++)
        printf("%lld\n",(qq[i].ans*invn%mod-qq[i].x*qq[i].x%mod*invn%mod*invn%mod+mod)%mod);
    return 0;
}

Pictures with Kittens (hard version)

1.单调队列优化dp。

2.用 f[i][j]​ 表示前 i 个数一共取 j​ 个且第 i​ 个一定取所得到的最大答案。

不难写出转移方程:f[i][j]=max{f[p][j−1]}+a[i](i−k≤p<i)

考虑优化:

注意到可以转移的区间长度不变,要求的是区间 [i−k,i−1]​​ 内的最小值,可以采用单调队列。

tips:通过单调队列,第一次更新即为最优答案,如果不进行初始化,但是本题可能会存在一种情况,即非法情况可能转移至合法情况,导致答案非法,说的清楚一点,即转移中可能会存在同一个数被加多次的情况,开始把 f​ 数组赋为负无穷,否则得到的答案可能比正确答案大。

记得特判。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5010;
int n,k,x,le=1,ri=0;
ll f[N][N];
int q[N],a[N];
int main(){
	scanf("%d%d%d",&n,&k,&x);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	memset(f,0xcf,sizeof(f));
	if(n/k>x){
		puts("-1");
		return 0;
	}
	f[0][0]=0;
	for(int j=1;j<=x;j++){
		le=1,ri=0;
		q[++ri]=0;
		for(int i=1;i<=n;i++){
			while(le<=ri&&q[le]+k<i) le++;
			f[i][j]=f[q[le]][j-1]+a[i];
			while(le<=ri&&f[i][j-1]>=f[q[ri]][j-1]) ri--;
			q[++ri]=i;
		}
	}
	ll ans=0;
	for(int i=n-k+1;i<=n;i++) ans=max(ans,f[i][x]);
	printf("%lld\n",ans);
	return 0;
}

Bookshelf G

1.线段树优化dp(怎么这么多dp!)

2.设 fi​ 表示前 i 本书满足条件时书架高度和的最小值。那么对于当前位置 i,满足宽度限制的最小位置 w 一定不减,可以用指针维护。显然有 fi​=min{fj−1​+max{Hj​,⋯,Hi​}}(w≤j≤i)。

把原来的 Hj​ 变为 Hj​∼Hi​ 的最大值,显然要动态维护 Hw​∼Hi​ 的值,只需支持对 H 后缀取 max⁡ 操作。由于 H 的最大值是不增的,所以取 ⁡max 相当于对一段后缀做区间覆盖。

线段树上二分找到当前要取 max 的分界点,剩下的前缀不会被影响。还需维护区间内 fi 的 ⁡min 和 fi​+Hi​ 的 ⁡min,以及查询 w∼i 中最小的 fi​+Hi​。

都可以用线段树来维护。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
struct node{
	int l,r,f,sum,lazy;
}e[N<<2];
int n,w[N],h[N],m,f[N];
int pre[N],st[N],top,L;
void pushup(int u){
	e[u].f=min(e[u<<1].f,e[u<<1|1].f);
	e[u].sum=min(e[u<<1].sum,e[u<<1|1].sum);
	return;
}
void pushdown(int u){
	if(e[u].lazy){
		e[u<<1].lazy=e[u<<1|1].lazy=e[u].lazy;
		e[u<<1].sum=e[u<<1].f+e[u].lazy;
		e[u<<1|1].sum=e[u<<1|1].f+e[u].lazy;
		e[u].lazy=0;
	}
	return;
}
void build(int u,int l,int r){
	e[u].l=l,e[u].r=r;
	if(l==r) return;
	int mid=l+r>>1;
	build(u<<1,l,mid),build(u<<1|1,mid+1,r);
	return;
}
void insert(int u,int x,int val){
	if(e[u].l==e[u].r){
		e[u].f=e[u].sum=val;
		return;
	}
	pushdown(u);
	int mid=e[u].l+e[u].r>>1;
	if(x<=mid) insert(u<<1,x,val);
	else insert(u<<1|1,x,val);
	pushup(u);
	return;
}
void update(int u,int l,int r,int val){
	if(l<=e[u].l && e[u].r<=r){
		e[u].lazy=val;
		e[u].sum=e[u].f+val;
		return;
	} 
	pushdown(u);
	int mid=e[u].l+e[u].r>>1;
	if(l<=mid) update(u<<1,l,r,val);
	if(r>mid) update(u<<1|1,l,r,val);
	pushup(u);
	return;
}
int query(int u,int l,int r){
	if(l<=e[u].l && e[u].r<=r) return e[u].sum;
	pushdown(u);
	int mid=e[u].l+e[u].r>>1;
	int res=1e18;
	if(l<=mid) res=min(res,query(u<<1,l,r));
	if(r>mid) res=min(res,query(u<<1|1,l,r));
	return res;
}
signed main(){
	cin>>n>>L;
	for(int i=1;i<=n;i++) cin>>h[i]>>w[i];
	for(int i=2;i<=n;i++) w[i]+=w[i-1];
	for(int i=1;i<=n;i++){
		while(top&&h[i]>h[st[top]]) top--;
		if(top) pre[i]=st[top];
		else pre[i]=1;
		st[++top]=i;
	}
	build(1,1,n);
	int head=0,nowmaxn=0;
	for(int i=1;i<=n;i++){
		nowmaxn=max(nowmaxn,h[i]);
		while(w[i]-w[head]>L) head++;
		update(1,pre[i],i-1,h[i]);
		if(head==0){
			f[i]=query(1,head+1,i-1);
			f[i]=min(f[i],nowmaxn);
		}
		else f[i]=query(1,head,i-1);
		insert(1,i,f[i]);
	}
	printf("%lld\n",f[n]);
	return 0;
}

总之,dp题挺多的(1道背包,1道单调队列优化,1道线段树优化),不太好写暴力,就当练习dp了。


http://www.kler.cn/a/405908.html

相关文章:

  • 经典算法:查找与排序
  • CSP/信奥赛C++语法基础刷题训练(22):洛谷P1075:[NOIP2012 普及组] 质因数分解
  • Elasticsearch 开放推理 API 增加了对 IBM watsonx.ai Slate 嵌入模型的支持
  • python小课堂(一)
  • gitlab:使用脚本批量下载项目,实现全项目检索
  • Docker3:docker基础1
  • H.265流媒体播放器EasyPlayer.js网页全终端安防视频流媒体播放器可以播放本地视频吗
  • Linux空口抓包方法
  • 数字图像处理(2):Verilog基础语法
  • 【React】React Router:深入理解前端路由的工作原理
  • 常用的消息中间件
  • Python编程艺术:优雅与实用的完美平衡(推导式)
  • 《OpenCV 图像基础操作全解析:从读取到像素处理与 ROI 应用》
  • hbase mongodb hive starrocks比较
  • 专题十一——栈
  • 17种Kubernetes安全检测工具详解
  • 解决绿盟漏洞扫描 gateway、nacos、springboot tomcat检测到目标主机可能存在缓慢的HTTP拒绝服务攻击问题
  • Node基本使用
  • Linux KASLR 地址偏移
  • C语言:数组转换指针的时机
  • Sparrow系列拓展篇:对信号量应用问题的深入讨论
  • Spring Cloud OpenFeign 声明式服务调用与负载均衡组件
  • React——useReducer
  • 3D模型平台行业全面深入分析
  • 【DQ Robotics】二次规划控制
  • 金融量化交易模型的探索与发展