题意:

给定一段长为N的序列,选取其中的至多M段使这些子段和最大。


当N=1000时,我们可以采用动态规划解法

令\(dp[i][j][k]\)代表当前选至位置\(i\)处于第\(j\)段当前是否选取(1选0不选)

则转移为

\(dp[i][j][0]=max(dp[i-1][j][1],dp[i-1][j][0])\)

\(dp[i][j][1]=max(dp[i-1][j-1][0],dp[i-1][j][1])+score[i]\)

其中\(i\)的一维可以滚动掉

Code:

#include <cstdio>
#include <cstring>
int max(int x,int y){return x>y?x:y;}
const int N=503;
const int inf=0x3f3f3f3f;
int dp[N][N][2],n,k,score[N];
void init()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",score+i);
memset(dp,-0x3f,sizeof(dp));
dp[1][0][0]=0,dp[1][1][1]=score[1];
}
void work()
{
for(int i=2;i<=n;i++)
for(int j=0;j<=k;j++)
{
dp[i][j][0]=max(dp[i-1][j][1],dp[i-1][j][0]);
dp[i][j][1]=max(dp[i-1][j-1][0],dp[i-1][j][1])+score[i];
}
int ans=-inf;
for(int i=0;i<=k;i++)
ans=max(ans,max(dp[n][i][0],dp[n][i][1]));
printf("%d\n",ans);
}
int main()
{
init();
work();
return 0;
}

当N=100,000时,我们可以采用贪心解法

注意到每次选取时,一段连续的正数或者连续的负数,要么我们不取选它,要么全部选走。

我们先缩个点,使序列变成正负数交错的,其中,第一项和最后一项的负数我们一定不去选择,故舍去。

设当前有\(k\)个正数。

则当\(M>=k\)时,直接选取\(k\)个正数即可。

当\(M<=k\)时,对每个正数,我们有两种选择,放弃它,或者跨过负数选择它。

则当选中一个负数\(a\)时,我们损失了\(-a\),当放弃一个正数\(b\)时,我们损失了\(b\)

则不管哪种情况,我们只需要找到绝对值最小的一个数,选择它或者放弃它

采用二叉堆维护这个最小值

当这个最小值被操作时,其实等价于新产生了一个权值为 它和它旁边的两个元素的权值和 的新元素,这时候产生的新数列一定会少一个正数

值得一提的是,当第一项和最后一项是负数的时候,不一定一定减少一个正数,所以我们根据贪心不去选择这些点即可

合并元素可以采用链表进行维护

Code:(实在是不好看QAQ)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#define ll long long
#define P pair <ll,ll>
using namespace std;
const int N=1000010;
ll a[N],b[N],n0,k,n,pre[N],suc[N],cnt,used[N];
ll labs(ll x){return x>0?x:-x;}
void init()
{
scanf("%lld%lld",&n0,&k);
for(int i=1;i<=n0;i++)
scanf("%lld",a+i);
int k=0;a[0]=-1;
while(a[k]<0) k++;
for(int i=k;i<=n0;i++)
{
if(a[i]*a[i-1]>0)
b[n]+=a[i];
else
b[++n]=a[i];
}
while(n&&b[n]<0) n--;
for(int i=1;i<=n;i++)
{
pre[i]=i-1;
suc[i]=i+1;
}
suc[0]=1;
suc[n]=0;
}
priority_queue <P,vector <P >,greater <P > > q;
P p;
void work()
{
for(int i=1;i<=n;i++)
{
if(b[i]>0) cnt++;
p.first=labs(b[i]);
p.second=i;
q.push(p);
}
while(cnt>k)
{
int pos=q.top().second;
q.pop();
if(used[pos]) continue;
if(!suc[pos]&&b[pos]<0)
{
suc[pre[pos]]=suc[pos];
pre[suc[pos]]=pre[pos];
continue;
}
if(!pre[pos]&&b[pos]<0)
{
pre[suc[pos]]=pre[pos];
suc[pre[pos]]=suc[pos];
continue;
}
used[pre[pos]]=used[suc[pos]]=1;
b[pos]+=b[pre[pos]]+b[suc[pos]];
cnt--;
if(pre[pos])
{
pre[pos]=pre[pre[pos]];
suc[pre[pos]]=pos;
}
if(suc[pos])
{
suc[pos]=suc[suc[pos]];
pre[suc[pos]]=pos;
}
p.first=labs(b[pos]),p.second=pos;
q.push(p);
}
int now=suc[0];
ll ans=0;
while(now)
{
if(b[now]>0) ans+=b[now];
now=suc[now];
}
printf("%lld\n",ans);
}
int main()
{
init();
work();
return 0;
}

当N=1,000,000时,我们可以将算法近似优化到\(O(N)\)

具体大家可以参考出题人的题解

我在这里解释一下这个近似,原题解没有说道找到第\(k\)值的问题

主要就是利用基于快速排序思想的STL函数\(nth\_element\),可以在近似\(O(n)\)的复杂度找到第\(k\)值

关于第k值

代码我没写,感觉不太好写

洛谷 P2389 电脑班的裁员 解题报告的更多相关文章

  1. 洛谷P2389 电脑班的裁员(区间DP)

    题目背景 隔壁的新初一电脑班刚考过一场试,又到了BlingBling的裁员时间,老师把这项工作交给了ZZY来进行.而ZZY最近忙着刷题,就把这重要的任务交(tui)给了你. 题目描述 ZZY有独特的裁 ...

  2. 洛谷_Cx的故事_解题报告_第四题70

    1.并查集求最小生成树 Code: #include <stdio.h> #include <stdlib.h>   struct node {     long x,y,c; ...

  3. 洛谷 P2317 [HNOI2005]星际贸易 解题报告

    P2317 [HNOI2005]星际贸易 题目描述 输入输出格式 输入格式: 输出格式: 如果可以找到这样的方案,那么输出文件output.txt中包含两个整数X和Y.X表示贸易额,Y表示净利润并且两 ...

  4. 洛谷 P3802 小魔女帕琪 解题报告

    P3802 小魔女帕琪 题目背景 从前有一个聪明的小魔女帕琪,兴趣是狩猎吸血鬼. 帕琪能熟练使用七种属性(金.木.水.火.土.日.月)的魔法,除了能使用这么多种属性魔法外,她还能将两种以上属性组合,从 ...

  5. 洛谷 P2606 [ZJOI2010]排列计数 解题报告

    P2606 [ZJOI2010]排列计数 题目描述 称一个\(1,2,...,N\)的排列\(P_1,P_2...,P_n\)是\(Magic\)的,当且仅当对所以的\(2<=i<=N\) ...

  6. 洛谷1303 A*B Problem 解题报告

    洛谷1303 A*B Problem 本题地址:http://www.luogu.org/problem/show?pid=1303 题目描述 求两数的积. 输入输出格式 输入格式: 两个数 输出格式 ...

  7. 洛谷 P3084 [USACO13OPEN]照片Photo 解题报告

    [USACO13OPEN]照片Photo 题目描述 农夫约翰决定给站在一条线上的\(N(1 \le N \le 200,000)\)头奶牛制作一张全家福照片,\(N\)头奶牛编号\(1\)到\(N\) ...

  8. 洛谷 P1379 八数码难题 解题报告

    P1379 八数码难题 题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初 ...

  9. NOIP2015 D2T3 洛谷2680 BZOJ4326 运输计划 解题报告

    前言:个人认为这是历年NOIP中比较简单的最后一题了,因此将自己的思路与大家分享. 题目大意: 给一棵无根树,给出m条路径.允许将树上的一条边的权值改为0.求m条路径长度最大值的最小值.n,m< ...

随机推荐

  1. Hadoop初步简介

    Hadoop产生背景: 传统方式,我们使用数据库来对数据进行管理.可是随着数据量的增加,我们要对这个数据库中的海量数据进行处理, 从中提取出有效的信息,这时候面临的问题随之而来: 1.海量数据读取,采 ...

  2. SICP读书笔记 3.2

    SICP CONCLUSION 让我们举起杯,祝福那些将他们的思想镶嵌在重重括号之间的Lisp程序员 ! 祝我能够突破层层代码,找到住在里计算机的神灵! 目录 1. 构造过程抽象 2. 构造数据抽象 ...

  3. Linux文件句柄数调整

    首先介绍下Linux系统中"一切都是文件". 1. Linux系统文件句柄数概念 文件句柄(Windows) 文件描述符(Unix/Linux):file discriptor,f ...

  4. Hyperledger Fabric Ledger——账本总账

    Ledger Ledger(账本)即所有的state transitions(状态切换),是有序且不可篡改的.state transitions(状态切换)是由参与方提交的chaincode(智能合约 ...

  5. mongoose和mongodb的几篇文章 (ObjectId,ref)

    http://mongoosejs.com/docs/populate.html http://stackoverflow.com/questions/6578178/node-js-mongoose ...

  6. C# Linq找不到行或已更改

    前段时间工作中的一个新需求,有机会用到了Linq to SQL.使用后的第一感觉,就是方便很多,也为整个项目节约了一大把的开发时间,甚至代码量也少了很多.不过在程序的实际运行中,始终会遇到一些莫名其妙 ...

  7. 我对git的认识

    Git 真的是不了解 也没听说过git 所以真的不知道从何谈起 所以就参考度娘啦! Git是一个开源的分布式版本控制系统,用以有效.高速的处理从很小到非常大的项目版本管理.Git 是 Linus To ...

  8. Alpha阶段博客链接

    博客链接 团队项目启程篇章:http://www.cnblogs.com/liuliudashun/p/5968194.html 团队项目开发篇章1:http://www.cnblogs.com/li ...

  9. 简单复利计算java板

    一.要求: 1.客户说:帮我开发一个复利计算软件. 2如果按照单利计算,本息又是多少呢? 3.假如30年之后要筹措到300万元的养老金,平均的年回报率是3%,那么,现在必须投入的本金是多少呢? 4.利 ...

  10. sqlserver 对比数据库表是否完全一致的简单方法

    1. 使用数据库的工具进行处理 tablediff.exe 工具目录 C:\Program Files\Microsoft SQL Server\\COM 工具使用说明 tablediff.exe - ...