• [bzoj2288]【POJ Challenge】生日礼物_贪心_堆


    【POJ Challenge】生日礼物

    题目大意:给定一个长度为$n$的序列,允许选择不超过$m$个连续的部分,求元素之和的最大值。

    数据范围:$1le n, mle 10^5$。


    题解

    显然的一步转化,就是把连续的、同符号的元素求和变成一个。

    这样就变成了一串正负号交替的序列。

    现在把所有正数都加一起,如果满足条件就直接输出。

    不满足的话,我们发现:

    我们可以选取一个负数,这样可以合并左右两个正数。

    我们也可以删掉一个正数。

    以上两个操作,都会使我们的选取的个数$- -$。

    至于到底应该怎么选呢?

    就弄一个堆,每次拿出来代价最小的操作就好。

    代码

    #include <bits/stdc++.h>
    
    #define N 100010 
    
    using namespace std;
    
    int a[N], b[N], nxt[N], pre[N];
    
    bool vis[N];
    
    char *p1, *p2, buf[100000];
    
    #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ )
    
    int rd() {
    	int x = 0, f = 1;
    	char c = nc();
    	while (c < 48) {
    		if (c == '-')
    			f = -1;
    		c = nc();
    	}
    	while (c > 47) {
    		x = (((x << 2) + x) << 1) + (c ^ 48), c = nc();
    	}
    	return x * f;
    }
    
    struct Node {
    	int val, id;
    	friend bool operator < (const Node &a, const Node &b) {
    		return a.val > b.val;
    	}
    };
    
    priority_queue<Node> q;
    
    int main() {
    	int n = rd(), m = rd();
    	for (int i = 1; i <= n; i ++ ) {
    		b[i] = rd();
    	}
    	int n1 = 1;
    	a[1] = b[1];
    	for (int i = 2; i <= n; i ++ ) {
    		if ((a[n1] <= 0 && b[i] <= 0) || (a[n1] >= 0 && b[i] >= 0)) a[n1] += b[i];
    		else a[ ++ n1] = b[i];
    	}
    	if (a[n1] <= 0) {
    		n1 -- ;
    	}
    	if (a[1] <= 0) {
    		for (int i = 1; i < n1; i ++ ) {
    			a[i] = a[i + 1];
    		}
    		n1 -- ;
    	}
    	n = n1;
    	int ans = 0, sum = 0;
    	for (int i = 1; i <= n; i ++ ) {
    		if (a[i] > 0) {
    			sum ++ ;
    			ans += a[i];
    		}
    		Node mdl;
    		mdl.val = abs(a[i]);
    		mdl.id = i;
    		q.push(mdl);	
    		nxt[i] = i + 1;
    		pre[i] = i - 1;
    		a[i] = abs(a[i]);
    	}
    	// cout << ans << endl ;
    	// cout << sum << endl ;
    	nxt[n] = pre[1] = 0;
    	if (sum <= m) {
    		cout << ans << endl ;
    		return 0;
    	}
    	m = sum - m;
    	for (int i = 1; i <= m; i ++ ) {
    		Node mdl = q.top();
    		q.pop();
    		while (vis[mdl.id] && !q.empty()) {
    			mdl = q.top();
    			q.pop();
    		}
    		// cout << mdl.val << endl ;
    		if (vis[mdl.id])
    			break;
    		ans -= mdl.val;
    		if (q.empty())
    			break;
    		int tmp = mdl.id;
    		if (!pre[tmp]) {
    			vis[tmp] = true;
    			vis[nxt[tmp]] = true;
    			pre[nxt[nxt[tmp]]] = 0;
    		}
    		else if(!nxt[tmp]) {
    			vis[tmp] = true;
    			vis[pre[tmp]] = true;
    			nxt[pre[pre[tmp]]] = 0;
    		}
    		else {
    			vis[nxt[tmp]] = true;
    			vis[pre[tmp]] = true;
    			mdl.val = a[tmp] = a[nxt[tmp]] + a[pre[tmp]] - a[tmp];
    			if (nxt[nxt[tmp]])
    				pre[nxt[nxt[tmp]]] = tmp;
    			if (pre[pre[tmp]])
    				nxt[pre[pre[tmp]]] = tmp;
    			pre[tmp] = pre[pre[tmp]];
    			nxt[tmp] = nxt[nxt[tmp]];
    			q.push(mdl);
    		}
    	}
    	cout << ans << endl ;
    	return 0;
    }
    

    小结:这玩意儿好像叫模拟费用流吧,不会不会有空学/cy

  • 相关阅读:
    如果你正在找工作,也许这七个方法会帮到你
    WebSocket 浅析
    关系数据库涉及中的范式与反范式
    MySQL字段类型与合理的选择字段类型
    ER图,数据建模与数据字典
    详解慢查询
    MySQL的最佳索引攻略
    后端技术演进
    MySQL主从复制(BinaryLog)
    MySQL读写分离
  • 原文地址:https://www.cnblogs.com/ShuraK/p/11255632.html
Copyright © 2020-2023  润新知