【离散化】【DP】洛谷P4945 最后的战役

本文介绍了一种结合离散化与动态规划(DP)的算法策略,用于解决特定类型的问题。通过预处理和使用前缀和及前缀最大值,文章详细解释了如何优化DP方程,实现高效的计算过程。代码示例展示了如何应用这些技巧来解决具有大量数据的复杂问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

…?


这是同份代码。
图源自洛谷评测结果页面


题目描述

图源洛谷P4945
图源洛谷P4945


一些样例

#1
输入
4 1
1 2
2 3
1 2
3 8
3 2 1 3
输出
21
#2
输入
8 3
1 2
2 5
3 2
2 3
1 4
1 6
2 2
3 3
1 3 2 1 4 5 2 1
输出
57
#3
输入
10 3
9 9
8 8
5 7
6 6
5 5
5 5
3 3
2 2
1 1
9 9
1 2 3 5 5 5 6 7 8 9
输出
124

思路

100%数据:n<=5×104,m<=500,0<pi<=104,0<ki<=109,0<xi<=109
ki很大要用离散化。(话说我这离散怎么那么奇怪)
然后用前缀和和前缀最大值预处理下。
dp方程就按着题目所述瞎写:设i为层数,j为施法次数

f[i][j] = max(f[i][j], f[i-1][j]+cz1[X[i]]);
//第一种操作,加上已经预处理的所有X[i]类型的值
f[i][j] = max(f[i][j], f[i-1][j]+maxx[i]);
//第二种操作,加上已经预处理的经过的保护层的最大值
f[i][j] = max(f[i][j], f[i-2][j-1]+max(cz1[X[i]],maxx[i])*2);
//第三种操作,i-2是因为i-1的地方因为要施法所以不能取值

代码

又变丑了()

#include<cstdio>
#include<algorith>
using namespace std;
int n,m,cnt,maxans,im3,l[10001],k[50001],p[50001],X[50001],maxx[50001],cz1[50001],f[5][50001];
int read(){ //快读
	int x=0;
	char c;
	while(c>'9'||c<'0') c = getchar();
	while(c<='9'&&c>='0'){
		x = x*10+c-48;
		c = getchar();
	}
	return x;
}
int findd(int d)  //离散
{
	int ll=cnt, rr=1, mid;
	while(rr<=ll)
	{
		mid=(ll+rr)/2;
		if(d>l[mid]) rr=mid+1;
		if(d<l[mid]) ll=mid-1;
		if(d==l[mid]) return mid;
	}
}
void inn(){  //输入与预处理
	n = read(); m = read(); //输入
	for(int i=1; i<=n; ++i)
	{
		k[i]=read(); p[i]=read();
		l[++cnt] = k[i];  //离散
		maxx[i] = max(maxx[i-1],p[i]);//前缀最大值
	}
	for(int i=1; i<=n; ++i)
	{
		X[i] = read();
		l[++cnt] = X[i]; 
	} 
	sort(l+1,l+1+cnt);  //离散
	cnt=unique(l+1,l+cnt+1)-l-1;
	for(int i=1; i<=n; ++i)  //离散
	{
		X[i] = findd(X[i]);
		k[i] = findd(k[i]);
	}
}
void workk(){
	for(int i=1; i<=n; ++i)  //层数
	{
		cz1[k[i]] += p[i];  //求所有魔法种类为k[i]的值的和
		im3 = i%3;  //滚动数组
		for(int j=0; j<=m; ++j)  //施法次数
		{
	  	    f[im3][j] = max(f[im3][j], f[(i-1)%3][j]+cz1[X[i]]);  //如上所述
			f[im3][j] = max(f[im3][j], f[(i-1)%3][j]+maxx[i]);
			if(j*2>i||i==1) break;  //注意边界(这里写得好乱)
			if(j>0) f[im3][j] = max(f[imm3][j], f[(i-2)%3][j-1]+max(cz1[X[i]],maxx[i])*2);
			maxans = max(maxans,f[im3][j]);  //记录结果(这应该放到dp结束后的)
	    }
	}
}
int main()
{
	inn();
	workk();
	printf("%d",maxans);
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值