zoukankan      html  css  js  c++  java
  • [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

  • 相关阅读:
    DNS域名解析抓包分析
    Redis实现分布式锁
    Redis内存回收淘汰策略
    Redis缓存雪崩、击穿、穿透
    Redis内存碎片
    C++ 友元
    C++ const
    C++构造函数与析构函数调用虚函数
    C++类成员变量的初始化顺序
    C++ 类对象和类指针
  • 原文地址:https://www.cnblogs.com/ShuraK/p/11255632.html
Copyright © 2011-2022 走看看