51Nod 1052/1053/1115 最大M子段和V1/V2/V3

本文介绍了一种解决最大M子段和问题的算法,包括线性和环形序列的处理方法。通过预处理和贪心策略,实现了高效求解。适用于编程竞赛和技术面试。

V1

N个整数组成的序列a[1],a[2],a[3],…,a[n],将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的。如果M >= N个数中正数的个数,那么输出所有正数的和。N,M<=5000
例如:-2 11 -4 13 -5 -2,分为2段,11 -4 13一段,6一段,和为26。

V2

N个整数组成的序列a[1],a[2],a[3],…,a[n],将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的。如果M >= N个数中正数的个数,那么输出所有正数的和。 N,M<=50000
例如:-2 11 -4 13 -5 6 -2,分为2段,11 -4 13一段,6一段,和为26。
V3
环形最大M子段和,N个整数组成的序列排成一个环,a[1],a[2],a[3],…,a[n]( a[n], a[1]也可以算作1段),将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的。如果M>=N个数中正数的个数,那么输出所有正数的和。N,M<=100000
例如:-2 11 -4 13 -5 6 -1,分为2段,6 -1 -2 11一段,13一段,和为27。

这三道题非常类似可以用几乎同一个code过掉

我们直接先考虑V3比较好做

首先将所有连续且符号相同的数加在一起,那么我们得到一个正负交替的环

如果不考虑限制,显然取所有正数最优

现在考虑限制,我们假设环上正数c个

那么就要减少M-c段,考虑减少段数的手段

1.删去一段

2.将两端合并成为一段

这个时候发现问题可以转化为经典的种花问题:

将所有的正数变为负数,在N个数(全部为负)中选出M-c个最大的,而相邻的不能选

选出来的数加上原本所有正数的和就是答案

直接套用原来的可撤销贪心做法就可以了,v2就多加一个权值为-∞的0号节点即可

V2code

#pragma GCC opitmize("O3")
#pragma G++ opitmize("O3")
#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 50010
using namespace std;
int n,m,t=0,l[N],r[N]; long long S=0,s[N],v[N];
priority_queue<pair<long long,int> > q; bool vis[N]={0};
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%lld",v+i);
		if(!t && v[i]<0) continue; else
		if(!t || (v[i]>=0)!=(s[t]>=0)) s[++t]=v[i]; else s[t]+=v[i];
	}
	while(s[t]<0) s[t--]=0;
	for(int i=1;i<=t;++i){
		l[i]=i-1; r[i]=i+1;
		if(i&1){
			S+=s[i]; s[i]=-s[i];
			q.push(make_pair(s[i],i));
		} else q.push(make_pair(s[i],i));
	}
	r[t]=0; l[0]=t; r[0]=1;
	q.push(make_pair(s[0]=-1000000000000ll,0));
	if(m>(t>>1)) return 0&printf("%lld\n",S);
	pair<long long,int> x; int y;
	for(m=(t-(t>>1))-m;m--;){
		x=q.top(); q.pop();
		if(vis[y=x.second]){++m; continue;}
		else{
			S+=s[y]; s[y]=-s[y]+s[l[y]]+s[r[y]];
			q.push(make_pair(s[y],y));
			r[l[l[y]]]=y;
			l[r[r[y]]]=y;
			vis[l[y]]=1;
			vis[r[y]]=1;
			l[y]=l[l[y]];
			r[y]=r[r[y]];
		}
	}
	printf("%lld\n",max(0ll,S));
}
V3code

#pragma GCC opitmize("O3")
#pragma G++ opitmize("O3")
#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 200010
using namespace std;
int n,m,t=0,l[N],r[N],c; long long S=0,s[N],v[N];
priority_queue<pair<long long,int> > q; bool vis[N]={0};
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%lld",v+i);
		if(!t || (v[i]>=0)!=(s[t]>=0)) s[++t]=v[i]; else s[t]+=v[i];
	}
	if((s[1]>=0)==(s[t]>=0)) s[1]+=s[t--];
	for(int i=1;i<=t;++i){
		l[i]=i-1; r[i]=i+1;
		if(s[i]>=0){
			S+=s[i]; s[i]=-s[i]; c++;
			q.push(make_pair(s[i],i));
		} else q.push(make_pair(s[i],i));
	}
	r[t]=1; l[1]=t;
	if(m>=c) return 0&printf("%lld\n",S);
	pair<long long,int> x; int y;
	for(m=c-m;m--;){
		x=q.top(); q.pop();
		if(vis[y=x.second]){++m; continue;}
		else{
			S+=s[y]; s[y]=-s[y]+s[l[y]]+s[r[y]];
			q.push(make_pair(s[y],y));
			r[l[l[y]]]=y;
			l[r[r[y]]]=y;
			vis[l[y]]=1;
			vis[r[y]]=1;
			l[y]=l[l[y]];
			r[y]=r[r[y]];
		}
	}
	printf("%lld\n",max(0ll,S));
}


### Floyd算法的快速幂优化变种 Floyd-Warshall算法是一种经典的动态规划算法,用于计算图中所有节点对之间的最短路径。该算法的时间复杂度为 $ O(n^3) $,适用于稠密图较小规模的问题[^2]。标准Floyd算法的核心思想是逐步更新邻接矩阵,使得每一步都能考虑引入中间节点后是否能获得更短的路径。 其核心代码如下: ```cpp for (int k = 0; k < n; ++k) for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]); ``` 在某些特定问题中,例如**51nod 3225题解**中提到的内容,Floyd算法可能被优化或变形使用。例如,在处理图的幂次扩展时,可以通过类似“快速幂”的方法来加速Floyd算法的执行过程,从而求解多阶的最短路径问题。 #### 快速幂优化的Floyd变种 快速幂优化的思想来源于矩阵乘法中的幂运算优化策略。对于某些图问题,尤其是当图的边权表示某种“转移代价”,而我们希望求出经过最多 $ k $ 步转移后的最短路径时,可以将Floyd算法与矩阵快速幂结合。 具体来说,定义一种类似于矩阵乘法的操作,其中加法对应路径长度的相加,乘法对应取最小值操作(即 `min(a + b, c)`)。通过这种方式,图的邻接矩阵可以视为一个“广义矩阵乘法”结构下的对象,从而支持快速幂运算。 以下是一个基于此思想的C++实现示例: ```cpp #include <bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; const int MAXN = 105; struct Matrix { int mat[MAXN][MAXN]; Matrix() { for (int i = 0; i < MAXN; ++i) for (int j = 0; j < MAXN; ++j) mat[i][j] = INF; } }; Matrix multiply(const Matrix& A, const Matrix& B) { Matrix res; for (int i = 0; i < MAXN; ++i) { for (int k = 0; k < MAXN; ++k) { if (A.mat[i][k] == INF) continue; for (int j = 0; j < MAXN; ++j) { if (B.mat[k][j] == INF) continue; res.mat[i][j] = min(res.mat[i][j], A.mat[i][k] + B.mat[k][j]); } } } return res; } Matrix matrixPower(const Matrix& base, int power) { Matrix result; // 初始化为单位矩阵:相当于路径长度为0的自环 for (int i = 0; i < MAXN; ++i) result.mat[i][i] = 0; Matrix curr = base; while (power > 0) { if (power & 1) result = multiply(result, curr); curr = multiply(curr, curr); power >>= 1; } return result; } ``` 这种实现方式特别适用于需要多次应用图的“转移”并希望在 $ O(\log k) $ 时间内完成的情形。例如,51nod 3225题目中,可能存在对图进行多次迭代更新的需求,此时采用快速幂优化的Floyd算法能够显著提升效率。 #### 应用场景与优势 - **图的幂次路径问题**:如给定一个图,要求找出经过最多 $ k $ 次跳跃后任意两点之间的最短路径。 - **动态网络建模**:在某些系统中,网络结构会随时间变化,但变化具有周期性或可预测性,此时可以用快速幂构造“超步长”的路径矩阵。 - **稀疏图优化**:虽然Floyd本身适合稠密图,但在某些稀疏图中,结合快速幂稀疏矩阵优化也能取得不错的效果。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值