[SDOI2010]星际竞速

[SDOI2010]星际竞速

https://ac.nowcoder.com/acm/problem/20339

建图,网络流,最小费用最大流

题意:

图片说明

分析:

雨神说过,图论题难就难在建图上。这题我觉得建图还是蛮难的。

我们捕捉一下重要信息:
从一个额外的点出发,恰好经过每一个点一次,可以进行跳跃,永远是小点连大点。

我们不难明白,所谓额外的点其实就是时空跳跃的中介点,也同时是起点。
同时,我们也很本能的想将所有的点向汇点t连一条c=1,d=0的有向边。
然后求最小费用最大流。
类似这样:

但是,很明显没有这么简单。
我们会发现,我们仅仅实现了从S跳到每一个节点上去。
我们都知道,在最小费用流算法中,我们是每一次都取最短路的。
我们无法保证走了一次的点不会在被取到。
也无法保证能够实现点分叉的情况。
比如图中e(1,2)于e(1,3)
假设,我们取费用流时,第一次取到了1。
然后要取2时,我们有两条路。
1.时空跳跃到2
2.从1到2
第一种尚且还好,但如果是第二种的话,那么e(s,1)的c必须>1了。
且我们的e(s,1)的d算了两次,亏了!!
这种情况无法满足

再来看下面遇到分叉的情况。
1已选,3通过e(1,3)已选,下面该是2了。因为我们已经有路s->1->3了
即在分叉点1处我们已经向3拐了,因此s->1->2是不存在的。但是,我们又怎么区分呢?

做不到啊!!

对上面两种气矿进行总结:
1.我们对于每一个节点,他关于其伸出去的枝杈只能走一个。
2.在搜寻最短路时,我们不应从最开始就开始搜,还应该增加从之前的节点开始搜索。

我们看第一点,这很简单我们可以对其进行限流如:
图片说明
那第二点呢,我们的起点时始终不变的!永远是s,如果想改变只能连一条e(s,1) d=0;
因为大点是不可能到达小点的,所以我们不用担心。

这回和原来的图有些冲突,我们可以拆点来解决。

就像这样:
图片说明

s点向1~n连一条c=1,d=0的边
s点向n+1·2n连一条c=1,d=传送cost
n+1·2n向t连一条c=1,d=0的边
对边e(u,v),变为e(u,v+n)c=1,d=原cost

如此这般,快乐的跑一个最小费用最大流就好了。

玄学

代码如下:

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef pair<int, int> pii;
const int inf = 2e9;
const int max_n = 2000;
const int max_m = 40000;
int n, m;
struct edge
{
    int to, cap, cost, rev, next;
}E[max_m];
int head[max_n];
int cnt = 1;
void add(int from, int to, int cap, int cost) {
    E[cnt].to = to;E[cnt].cap = cap;
    E[cnt].cost = cost;E[cnt].rev = cnt + 1;
    E[cnt].next = head[from];
    head[from] = cnt++;
    E[cnt].to = from;E[cnt].cap = 0;
    E[cnt].cost = -cost;E[cnt].rev = cnt - 1;
    E[cnt].next = head[to];
    head[to] = cnt++;
}

int dist[max_n];
bool used[max_n];
pii fa[max_n];
bool spfa(int s, int t) {
    fill(dist, dist + n + 10, inf);
    fill(used, used + n + 10, false);
    queue<int> que;dist[s] = 0;
    que.push(s);used[s] = true;
    while (!que.empty()) {
        int u = que.front();que.pop();
        used[u] = false;
        for (int i = head[u];i;i = E[i].next) {
            edge& e = E[i];
            if (dist[e.to] <= dist[u] + e.cost || e.cap == 0)continue;
            dist[e.to] = dist[u] + e.cost;
            fa[e.to] = { u,i };
            if (used[e.to])continue;
            que.push(e.to);used[e.to] = true;
        }
    }return dist[t] != inf;
}

int matchpath(int s, int t,int& f) {
    int d = f;
    for (int i = t;i != s;i = fa[i].first) {
        int id = fa[i].second;
        d = min(d, E[id].cap);
    }for (int i = t;i != s;i = fa[i].first) {
        int id = fa[i].second;
        edge& e = E[id];
        e.cap -= d;
        E[e.rev].cap += d;
    }f -= d;
    return d * dist[t];
}
int mcf(int s,int t,int f) {
    int res = 0;int cost;
    while (f > 0 && spfa(s, t))
        res += matchpath(s, t, f);
    return res;
}

int main() {
    ios::sync_with_stdio(0);
    cin >> n >> m;
    int start = 2 * n + 1;int ed = start + 1;
    for (int i = 1;i <= n;i++) {
        int cost;cin >> cost;
        add(start, n + i, 1, cost);
        add(start, i, 1, 0);
        add(i + n, ed, 1, 0);
    }for (int i = 1;i <= m;i++) {
        int u, v, cost;cin >> u >> v >> cost;
        if (u > v)swap(u, v);
        add(u, v + n, 1, cost);
    }n = ed;
    cout << mcf(start, ed, n) << endl;
}
全部评论

相关推荐

11-15 17:19
湖南大学 Java
成果成果成果果:这是哪个公司的hr,这么离谱吗,我没见过用性别卡技术岗的,身边女性同学拿大厂offer的比比皆是
点赞 评论 收藏
分享
2 收藏 评论
分享
牛客网
牛客企业服务