序列选数(最大费用最大流 || 优先队列)

问题

  • 给定偶数个数 a [ 1 ] 、 a [ 2 ] 、 . . . 、 a [ n ] a[1]、a[2]、...、a[n] a[1]a[2]...a[n]
  • 分别输出从这n个数里选 1 、 2 、 3 、 … 、 n / 2 1、2、3、…、n/2 123n/2 个数时的和的最大值,选数规则是不能同时选相邻的数。
  • 1 ≤ a [ i ] ≤ 1000 , 2 ≤ n ≤ 100000 1\leq a[i] \leq 1000,2 \leq n \leq 100000 1a[i]10002n100000
  • 输入:
    1
    10
    737 757 860 778 371 591 20 241 768 442
  • 输出
    860
    1628
    2365
    2956
    2871

分析

  • 顶点数1e5,使用费用流则超时
  • 当第2个数选择768后,在x+768 >= 241+442无法满足时,将会选择768前后的241和442两个数,且放弃768(反悔操作)
  • 当选择了737后,就不可能再放弃737(无对应的反悔操作)
  • 网络流(可将in和out之间的数字顶点删除,数字成为in和out两顶点间路径的费用系数)
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
s
in
in
in
in
in
737
757
860
778
371
591
20
241
768
442
out
t
out
out
out
out
out

代码

优先队列

#include<bits/stdc++.h>
using namespace std;
const int MXN = 100010, inf = 0x3f3f3f3f;
struct Range{
	int l, pre, nxt, v;
	bool operator<(Range x)const{ return v < x.v;}
}r[MXN];
priority_queue<Range> q;
int main(){
	int t, n, ans, cnt;
	scanf("%d", &t);
	while(t--){
		ans = 0;
		while(!q.empty()) q.pop();
		scanf("%d", &n);
		for(int i = 1; i <= n; ++i){
			scanf("%d", &r[i].v);
			r[i].pre = i-1, r[i].nxt = i+1;
			q.push((Range){i, 0, 0, r[i].v});
		}
		cnt = n/2;
		while(!q.empty() && cnt){
			Range k = q.top();
			q.pop();
			Range &y = r[k.l];
			if(y.v != k.v) continue;
			ans += y.v, cnt--;
			printf("%d\n", ans);
			Range &x = r[y.pre], &z = r[y.nxt];
			if(y.pre < 1 && y.nxt > n) continue;
			else if(y.pre == 0) y.v = z.v = -inf, r[z.nxt].pre = 0;
			else if(y.nxt > n) x.v =y.v = -inf, r[x.pre].nxt = y.nxt;
			else{
				x.v += z.v - y.v, x.nxt = z.nxt;
				y.v = z.v = -inf;
				r[z.nxt].pre = y.pre;
				k.v = x.v, k.l = y.pre;
				q.push(k);
			}
		}
	}
    return 0;
}

最大费用最大流【TLE】

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f, MXN = 100010, MX = 1000;
int d[MXN<<1], pre[MXN<<1], flow[MXN<<1], v[MXN<<1];
int tot,  head[MXN<<1];
struct Edge{int to, nxt, w, c;}edge[MXN<<3]; // 前向星
void addEdge(int u, int v, int w, int c){    // 加边
	Edge &e = edge[++tot];
	e.to = v, e.nxt = head[u], e.w = w, e.c = c, head[u] = tot;
}
void add(int u, int v, int w, int c){        // 加原边 和 反向边
	addEdge(u, v, w, c), addEdge(v, u, 0, -c);
}
void spfa(int s, int t){
    queue<int> q;
	int f;
	memset(d, 0xcf, sizeof d), memset(v, 0, sizeof v);
    v[s] = 1, d[s] = 0, flow[s] = inf, q.push(s);
    while(!q.empty()){
        f = q.front(), q.pop(), v[f] = 0;
        for(int to, i = head[f]; ~i; i = edge[i].nxt){
			if(!edge[i].w) continue;
            to = edge[i].to;
            if(d[to] < d[f] + edge[i].c){
                d[to] = d[f] + edge[i].c;
				flow[to] = min(flow[f], edge[i].w);
				pre[to] = i;
                if(!v[to]) v[to] = 1, q.push(to);
            }
        }
    }
}
void update(int s, int t){
	for(int i, now = t; now != s; now = edge[i^1].to)
		i = pre[now], edge[i].w -= flow[t], edge[i^1].w += flow[t];
}
int main(){
	int t, a, maxflow, ans, n, k, S, T;
	scanf("%d", &t);
	while(t--){
		scanf("%d", &n);
		k = n/2, S = n+2, T = n+3, tot = 1, ans = 0, maxflow = 0;
		memset(head, -1, sizeof head);
		for(int i = 1; i <= k; ++i)
			scanf("%d", &a), add(i, k+i, 1, a), scanf("%d", &a), add(i, k+i+1, 1, a);
		for(int i = 1; i <= k; ++i) add(S, i, 1, 0);
		for(int i = k+1; i <= n+1; ++i) add(i, T, 1, 0);
		for(int i = 1; i <= k; ++i){
			spfa(S, T), maxflow += flow[T], ans += flow[T]*d[T], update(S, T);
			printf("%d\n", ans);
		}
	}
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jpphy0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值