首页 > 其他分享 >区间dp

区间dp

时间:2025-01-18 14:44:54浏览次数:1  
标签:int 可以 枚举 区间 sum dp

区间dp

先在小区间上进行dp得到最优解,然后再合并小区间的最优解求得大区间的最优解,解题时,先解决小区间的问题,再将小区间合并为大区间,合并操作一般是将两个相邻区间合并
注:合并顺序从小区间到大区间,因该先从小到大枚举区间的长度,递推出j在哪里

一本通题解

T4:

啊啊啊为什么区间dp的题想根本想不出来啊啊啊

设 \(dp[i][j]\) 表示区间获得的最大价值是多少(注:不是将区间内全部删掉)

我们分情况讨论

考虑如果 \((i+1,j-1)\) 这段区间可以全部被删除,就考虑能不能将 \((i,j)\) 合并删除掉

如何判断 \((i+1,j-1)\) 这段区间可以全部被删除是否成立?

只要满足条件 \(dp[i+1][j-1]==\sum_{i=i+1}^{r-1} b[i]\) 即可(因为这一段可以全部删掉就自然可以获得这一段的价值总和)

因为正常的合并条件为 \(a[i]+a[j]<=k\),所以再次判断一下即可

如果 \((i+1,j-1)\) 这段区间不可以全部被删除

我们自然是将大区间拆分小区间的方法,就是正常区间dp做法,枚举断点k即可

T5:

推式子。。。

求最小值,经典操作,先把固定的东西丢掉

去掉根号

考虑用二维区间dp实现

这里有一点变式,因为这个不是从小区间推导到大区间,而是从前一次的状态推导到这一次,并不需要区间从小到大枚举,但是我们是倒推,从一个小矩形往边上加块,推得一个大区间,所以我们要把那些不合法还没有通过推导得到的状态设为inf

\(dp[i][j][x][y][k]\) 代表第k次转移,合并出来的块左上角为 \((i,j)\) 右下角为 \((x,y)\) 的最小代价,所以初始状态就是使 \(dp[i][j][x][y][0]\) 为这个块的权值,也可以理解为最后剩下来的块的大小

转移

这个是横着割竖着割不同的情况,枚举g,我们可以分别选择一个块s作为我们拼出来的块,另一个块t代表我们此次操作拼上去的块,所以本次的贡献既是 \(dp[s][k-1]+num[t]\) (num代表块的权值和)

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=10;
int n;
int a[N][N],num[N][N],sum[N][N],dp[N][N][N][N][20];
int query(int i,int j,int x,int y){
	int cnt=sum[x][y]+sum[i-1][j-1]-sum[i-1][y]-sum[x][j-1];
	return cnt*cnt;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=8;i++){
		for(int j=1;j<=8;j++){
			scanf("%d",&a[i][j]);
			num[i][j]=num[i][j-1]+a[i][j];
			sum[i][j]=sum[i-1][j]+num[i][j];
		}
	}
	memset(dp,0x3f3f3f,sizeof(dp));
	for(int i=1;i<=8;i++){
		for(int j=1;j<=8;j++){
			for(int x=i;x<=8;x++){
				for(int y=j;y<=8;y++){
					dp[i][j][x][y][0]=query(i,j,x,y);
				}
			}
		}
	}
	for(int k=1;k<n;k++){
		for(int lenx=1;lenx<=8;lenx++){
			for(int i=1;i+lenx-1<=8;i++){
				for(int leny=1;leny<=8;leny++){
					for(int j=1;j+leny-1<=8;j++){
						int x=i+lenx-1,y=j+leny-1;
						for(int g=i;g<x;g++){
							dp[i][j][x][y][k]=min(dp[i][j][g][y][k-1]+query(g+1,j,x,y),dp[i][j][x][y][k]);
							dp[i][j][x][y][k]=min(dp[g+1][j][x][y][k-1]+query(i,j,g,y),dp[i][j][x][y][k]);
						}
						for(int g=j;g<y;g++){
							dp[i][j][x][y][k]=min(dp[i][j][x][g][k-1]+query(i,g+1,x,y),dp[i][j][x][y][k]);
							dp[i][j][x][y][k]=min(dp[i][g+1][x][y][k-1]+query(i,j,x,g),dp[i][j][x][y][k]);
						}
					}
				}
			}
		}
	}
	double xb=(double)sum[8][8]/(double)n;
	// printf("%lf\n",xb);
	printf("%.3lf",sqrt((double)dp[1][1][8][8][n-1]/(double)n-xb*xb));
}

T6:

为什么这么简单的题我都想不出来!!!

只能从两边删数可以看成从一个中心区间拓展到两个大区间的过程

我们可以对于区间 \((i,j)\) 分为两种情况

1.直接删去区间

2.分别删去
枚举k,正常转移即可

T7:

差一点就想到了啊啊啊啊啊啊啊
没有想起来区间dp要枚举k呜呜呜

贪心策略:
最有情况下,必定将一个狼一次性消灭,证明因为把一只狼消灭到一半再去攻击其他的狼受到伤害必定不小于一次性消灭掉的伤害

我们设 \(dp[i][j]\) 为删去 \((i,j)\) 受到伤害最小值

于是只要枚举攻击的狼k,受到的伤害为 \((b[i-1]+b[j+1]+a[k])*c[i]\) (\(c[i]\) 为受到攻击的次数)

转移式子

for(int k=i;k<=j;k++){
		dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]+(a[k]+b[i-1]+b[j+1])*h[k]);
}

注意初始化时要把所有状态设为inf

然后对于 \(dp[i][i]=(a[i]+b[i-1]+b[i+1])*c[i]\)

注意因为我的代码还有一点比较特殊就是会访问到 \(dp[i+1][i],dp[i][i-1]\) 所以将这些赋值为0即可

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=405;
int n,atk;
int a[N],b[N],h[N],dp[N][N];
int main(){
	scanf("%d%d",&n,&atk);
	for(int i=1;i<=n;i++){
		scanf("%d%d%d",&a[i],&b[i],&h[i]);
		h[i]=(int)ceil((double)h[i]/(double)(atk));
	}
	memset(dp,0x3f3f3f3f,sizeof(dp));
	for(int len=1;len<=n;len++){
		for(int i=1;i+len-1<=n;i++){
			int j=i+len-1;
			if(len==1){
				dp[i][j]=(a[i]+b[i-1]+b[i+1])*h[i];
				dp[i][i-1]=dp[i+1][i]=0;
				continue;
			}
			for(int k=i;k<=j;k++){
				dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]+(a[k]+b[i-1]+b[j+1])*h[k]);
			}
		}
	}
	printf("%d",dp[1][n]);
}

T8:

我们把一整块都为白色的贡献设为0,否则设为 \(\max(n,m)\)

然后二维区间dp转移即可

T9:

设 \(dp[i][j]\) 为男生选i,女生选j,所获取到的最大愉悦度,至于为什么这个dp没有后效性。。。我也不知道。。。

然后我们可以枚举上一维是由选哪两个人,得来的,可以枚举不选前一段男生或不选前一段女生的长度,具体转移式子可以参考ybt

这里为什么可以只枚举删去一段连续的女生或男生而不是两边都删呢,我们会发现这因该是包不优的,因为我们可以在两边都不选的一堆男生和女生中插进来一对,一定比之前更优

这里我之前有一个问题,就是这里为什么要选取边界条件设为-inf呢

for(int i=1;i<=n+1;i++){
	dp[i][0]=dp[0][i]=-inf;
}

考虑首先在定义上它就不合法, \((0,0)\) 一定要配成一对,然后第0号男生女生就不可以再配对其余男生或女生了,其次,如果说它可以这样转移的话,会有一部分算不上贡献,如下

最终代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=305,inf=1e18+5;
int n;
int a[N],suma[N],sumb[N],b[N],dp[N][N];
signed main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		suma[i]=suma[i-1]+a[i];
	}
	for(int i=1;i<=n;i++){
		scanf("%lld",&b[i]);
		sumb[i]=sumb[i-1]+b[i];
	}
	suma[n+1]=suma[n];
	sumb[n+1]=sumb[n];
	for(int i=1;i<=n+1;i++){
		dp[i][0]=dp[0][i]=-inf;
	}
	for(int i=1;i<=n+1;i++){
		for(int j=1;j<=n+1;j++){
			dp[i][j]=dp[i-1][j-1]+a[i]*b[j];
			for(int k=1;k<i;k++){
				dp[i][j]=max(dp[k-1][j-1]+a[i]*b[j]-(suma[i-1]-suma[k-1])*(suma[i-1]-suma[k-1]),dp[i][j]);
			}
			for(int k=1;k<j;k++){
				dp[i][j]=max(dp[i-1][k-1]+a[i]*b[j]-(sumb[j-1]-sumb[k-1])*(sumb[j-1]-sumb[k-1]),dp[i][j]);
			}
		}
	}
	printf("%lld",dp[n+1][n+1]);
}

标签:int,可以,枚举,区间,sum,dp
From: https://www.cnblogs.com/zcxnb/p/18678442

相关文章

  • 树形dp
    树形dp树上做dp非常常见,因为树本身有子结构性质(树和子树)一般解题思路:先把树转化为有根树(如果不连通的树,就加一个虚拟根,它连接所有孤立的树),然后在做dfs,递归到叶子节点,再一层层返回信息,就在这一步做dfsP2015二叉苹果树定义状态\(dp[u][j]\)表示以节点u为根的子树上留j条边时......
  • 状压dp
    状压dp应用背景以集合为状态,集合一般可以用二进制表示,用二进制的位运算处理集合问题一般是指数复杂度的,例如:1.子集问题,设n个元素没有先后关系,那么一共有\(2^n\)个子集;2.排列问题,对所有n个元素进行全排列,共有\(n!\)个排列状态压缩:主要就是dp的一种状态,与dp转移关系不大位运......
  • 单调队列优化dp
    一本通题解T2:再也不想碰这道题了。。。写了一下午。我们先设状态\(dp[i][j]\)表示前i个刷匠,考虑了前\(j\)个木板后所获得的最大价值(\(j\)个木板可以有空余)。然后枚举前\(i\)个刷匠,枚举每一条木板。对于一条木板可以此刷匠根本不刷,或不刷当前木板,状转方程:\[dp[i][j]......
  • LCS(递归/记忆化/dp)
    题目链接:https://leetcode.cn/problems/longest-common-subsequence/TLE暴力递归+记忆化版本(基于字符串长度无优化版本)//注意text1[i1-1]==text2[i2-1]classSolution{public:intdp[1000][1000];intlongestCommonSubsequence(stringtext1,stringtext2){......
  • 漏洞预警 | WordPress Plugin Radio Player SSRF漏洞
    0x00漏洞编号CVE-2024-543850x01危险等级高危0x02漏洞概述WordPress插件RadioPlayer是一种简单而有效的解决方案,用于将实时流媒体音频添加到您的WordPress网站。0x03漏洞详情CVE-2024-54385漏洞类型:SSRF影响:获取敏感信息简述:RadioPlayer的/wp-admin/admin-......
  • 【韩国汉阳大学主办】第六届土木建筑及灾害防控国际学术会议暨第三届智慧城市建筑与基
    第六届土木建筑及灾害防控国际学术会议暨第三届智慧城市建筑与基础设施耐久性国际学术会议(CADPC&DuraBI2025)20256thInternationalConferenceonCivil,ArchitectureandDisasterPreventionandControl&3rdInternationalConferenceonDurabilityofBuildinga......
  • 斜率优化DP
    斜率优化DP例题HNOI2008玩具装箱朴素dp设\(dp_i\)表示前\(i\)个物品,分若干段的最小代价。状态转移方程为:\[dp_{i}=\min_{j<i}\left\{dp_{j}+\left(i-(j+1)+s_{i}-s_{j}-L\right)^{2}\right\}=\min_{j<i}\left\{dp_{j}+\left(s_{i}-s_{j}+i-j-1-L\right)^{2}\right\}......
  • 三大主流国际课程IBDP、AP、A-Level的区别是什么?-季遇教育
      最近咨询季遇老师的有不少孩子就读双轨制的学校的家长,还有很多想要体制内转轨的家长,都会问到如何选择合适的国际课程。今天就给大家介绍一下三大主流课程的区别!  IB课程  IB一共分为四个学段:IBPYP(小学)、IBMYP(初中)、IBDP(高中)、IBCP(职业教育)四个学段,其中IBDP......
  • CSP2025 - 树形 DP
    CSP2025-树形DPT1「MXOIRound1」城市这个“树上两点距离之和”很典,让我们想到换根DP。首先求出\(\text{siz}_u\)和\(d_u\),分别表示子树\(u\)的大小和子树所有点到\(u\)的距离之和。接下来求出整棵树的所有点到\(\boldsymbolu\)的距离之和。考虑用\(d_u\)......
  • ThreadPool解析
    Thread_Pool项目解析简介ThreadPool是一个轻量级的C++线程池实现,旨在简化多线程编程。项目分析我们首先上github的项目地址:https://github.com/progschj/ThreadPool,然后克隆项目到本地。点开项目的ThrealPool.h文件,查看源码:#ifndefTHREAD_POOL_H#defineTHREAD_POOL......