【NOIP 费用流】JZOJ_3029 观光公交

本文探讨了一个关于公交调度的问题,通过使用费用流算法来优化乘客旅行时间总和。在一个风景迷人的小城,观光公交车需服务大量游客,司机通过安装氮气加速器来减少旅行时间。文章详细介绍了如何构建费用流图并求解最优方案。

题目

题目太长请自行阅读。
风景迷人的小城YYY市,拥有nnn个美丽的景点。由于慕名而来的游客越来越多,YYY市特意安排了一辆观光公交车,为游客提供更便捷的交通服务。观光公交车在第000分钟出现在111号景点,随后依次前往 2、3、4……n2、3、4……n234n号景点。从第iii号景点开到第i+1i+1i+1号景点需要 DiD_iDi 分钟。任意时刻,公交车只能往前开,或在景点处等待。
设共有mmm个游客,每位游客需要乘车111次从一个景点到达另一个景点,第iii位游客在TiT_iTi 分钟来到景点AiA_iAi,希望乘车前往景点Bi(Ai&lt;Bi)B_i(Ai&lt;Bi)Bi(Ai<Bi)。为了使所有乘客都能顺利到达目的地,公交车在每站都必须等待需要从该景点出发的所有乘客都上车后才能出发开往下一景点。假设乘客上下车不需要时间。
一个乘客的旅行时间,等于他到达目的地的时刻减去他来到出发地的时刻。因为只有一辆观光车,有时候还要停下来等其他乘客,乘客们纷纷抱怨旅行时间太长了。于是聪明的司机 ZZZZZZ 给公交车安装了kkk个氮气加速器,每使用一个加速器,可以使其中一个DiD_iDi111。对于同一个DiD_iDi可以重复使用加速器,但是必须保证使用后DiDiDi大于等于000
那么ZZZZZZ该如何安排使用加速器,才能使所有乘客的旅行时间总和最小?

思路

先求出不加速的答案,再用费用流求出加速可以减去的时间,最后就可以得出答案了。
首先我们要求出几个东西lastlastlastdowndowndowngetgetget
其中lastilast_ilasti表示做第iii站最晚乘客的到达时间。
downidown_idowni表示第iii站下车乘客的数量。
getiget_igeti表示第iii站车到达的时间。
那么不加速的答案就为∑i=1mgetpi.b−pi.t\sum_{i=1}^{m}get_{p_i.b}-p_i.ti=1mgetpi.bpi.t
考虑费用流,我们这样构图:
s→s′s \rightarrow s&#x27;ss,容量为kkk,费用为000,限制了加速器的个数。
对于每一个点,我们把它们拆成iiii′i&#x27;i
s′→i′s&#x27; \rightarrow i&#x27;si,容量为did_idi,费用为000,表示最多用did_idi个氮气。
i→i′i \rightarrow i&#x27;ii,容量为max(geti−lasti,0)max(get_i-last_i,0)max(getilasti,0),费用为000,代表这个站可以使用这么多个氮气加速使得后面能受到影响,因为过早地到达也要等待巴士到来。
i′→i+1i&#x27; \rightarrow i+1ii+1,容量为infinfinf,费用为downi+1down_{i+1}downi+1,表示接受氮气可以使经过这条路的人都减少111个时间。
i+1→ti+1\rightarrow ti+1t,容量为infinfinf,费用为000,把流量流到汇点。

代码

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>

struct people{
	int t, a, b;
}p[100001];
struct node{
    int x, y, next, cost, flow;
}e[8001];
int n, m, k, ans, tot = 1;
int s, s1, t;
int d[1001], last[1001], get[1001], down[1001], head[2004], pre[2004], dis[2004], v[2004];

const int inf = 2147483647;

void add(int x, int y, int flow, int cost) {
    e[++tot].x = x;
	e[tot].y = y;
    e[tot].cost = cost;
    e[tot].flow = flow;
    e[tot].next = head[x];
	head[x] = tot;
    e[++tot].x = y;
    e[tot].y = x;
    e[tot].cost = -cost;
    e[tot].flow = 0;
    e[tot].next = head[y];
    head[y] = tot;
}

int spfa() {
    std::queue<int> q;
    int x, y;
    memset(dis, 127 / 3, sizeof(dis));
    memset(v, 0, sizeof(v));
    q.push(s);
	dis[s] = 0;
	v[s] = 1;
    while (q.size()) {
        x = q.front();
		q.pop();
		v[x] = 0;
        for (int i = head[x]; i; i = e[i].next) {
            if (!e[i].flow) continue;
            y = e[i].y;
            if (dis[y] > dis[x] + e[i].cost) {
                dis[y] = dis[x] + e[i].cost;
                pre[y] = i;
                if (!v[y]) {
                    v[y] = 1;
                    q.push(y);
                }
            }
        }
    }
    return dis[t] < 707406378;
}

void addflow() {
    int i = t, mn = 2147483647;
    while (pre[i]) {
        mn = std::min(mn, e[pre[i]].flow);
        i = e[pre[i]].x;
    }
	ans += dis[t] * mn;
    i = t;
    while (pre[i]) {
        e[pre[i]].flow -= mn;
        e[pre[i] ^ 1].flow += mn;
        i = e[pre[i]].x;
    }
}

int main() {
	scanf("%d %d %d", &n, &m, &k);
	for (int i = 1; i < n; i++)
		scanf("%d", &d[i]);
	for (int i = 1; i <= m; i++) {
		scanf("%d %d %d", &p[i].t, &p[i].a, &p[i].b);
		last[p[i].a] = std::max(p[i].t, last[p[i].a]);
		down[p[i].b]++;
	}
	for (int i = 1; i < n; i++)
		get[i + 1] = std::max(last[i], get[i]) + d[i];
	for (int i = 1; i <= m; i++)
		ans += get[p[i].b] - p[i].t;
	s = n * 2 + 1;
	s1 = n * 2 + 2;
	t = n * 2 + 3;
	add(s, s1, k, 0);
	for (int i = 1; i < n; i++) {
		add(s1, i + n, d[i], 0);
		add(i + n, i + 1, inf, -down[i + 1]);//改成负的跑最小费用最大流
		add(i + 1, t, inf, 0);
		add(i, i + n, std::max(get[i] - last[i], 0), 0);
	}
	while (spfa())
		addflow();
	printf("%d", ans);
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值