洛谷 P6061 P6061 [加油武汉]疫情调查(带权二分图匹配 + 费用流)

2020.7.17
今天一上午被几个新闻弄得心情非常不好。又是有人挑起来对留学生的排挤和歧视。平时岁月静好的时候没借口挤兑,现在有机会了赶紧来踹一脚,人内心最深处的丑恶本性被暴露得一览无遗。天天拿我们回不回来的事情做文章。且不说大部分人是不是回来了,出来学习也是花的家里的钱吧,颇有一种家里盘算着让在外打工的姐姐攒钱给弟弟买房的感觉。前一阵子我们回国的时候不少人以自己在家里蹲了两个月为荣,对我们嗤之以鼻。真正在前线拼命的医生都说我主张孩子们快回来。我一位在1月自己出资捐回去了几百套防护装备的同学看到微博知乎这些东西气得破口大骂当初瞎了眼为这群人的安危忙前忙后,忙了半天这群被保护的人才不在乎你的安危,如果不是为了那些在前线拼了命的将士,这种事情他绝不会再管了。

我在帮着武汉那边到处联系医院情况的时候,好些骂我们留学生的人除了在家里坐着什么也没干。到头来功劳反倒都是他们的了,什么叫无耻抢功,这就是!!我们当时在外有的是任务要干,为什么要去帮忙?因为那是我们的家,那里有一群正在苦苦支撑我们不受伤害的人,忘不了当时越洋电话里疾控负责人颤颤巍巍的声音,这是我们的初心。再看看3月份之后的评论吧,怎么可能不伤心,拿着别人的成就居功自傲的人可要点儿脸吧。就算个体级别,我刷过的题,流过的汗也比目前cs专业多数大学生多了,有什么资格来瞧不起我们??嗯?我们都回去了,怕是某些人只能去带专了呢。我觉得如果0202年了还有人拿出不出国这件事来评判一个人的道德水准,那说明我们对于知识的普及仍然任重道远,道阻且长。

这道题是二分图带权匹配,可以用费用流,which我前一阵子从群里听说了的,所以就不学匈牙利了,感觉虽然是省选题目但是的确没啥思维含量,就是问你街道巡逻,如果停留在街道花费是Au,如果从这个街道去另一个费用那就是wi。有无限个巡逻员,首先非常显然的是我们需要建两层图,从u到v是一层。一层就是加个n流出空档呗。为什么分层呢?因为这是有向边,所以会出现回边的情况,如果不用层分离那么spfa它就死了(负权回路环)。然后看看哈,首先从u到v的边因为可能被走多次所以流量是INF,然后继续考虑如果停留的问题,那就是自己到自己呗,费用Au,然后容量为INF。题目中说还要回去,所以要加上跨层回去的边,走过了费用为0,最后超级源和汇相连,跑一边费用流就行了,我感觉不需要什么特别的手段吧,直接上spfa就行了,dijkstra说是vlogv,然而很多时候并没有比n方快,不出菊花图,管他呢。记得开long long,两年ACM一场空,不开long long见祖宗!!

代码:

#include <bits/stdc++.h>
using namespace std;
#define limit (1000000 + 5)//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-6
#define FASTIO  ios::sync_with_stdio(false);cin.tie(0);
#define ff(a) printf("%d\n",a );
#define pi(a,b) pair<a,b>
#define rep(i, a, b) for(ll i = a; i <= b ; ++i)
#define per(i, a, b) for(ll i = b ; i >= a ; --i)
#define MOD 998244353
#define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next)
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\acm_01\\data.txt", "rt", stdin)
#define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\acm_01\\dabiao.txt", "wt", stdout)
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
    ll sign = 1, x = 0;char s = getchar();
    while(s > '9' || s < '0' ){if(s == '-')sign = -1;s = getchar();}
    while(s >= '0' && s <= '9'){x = (x << 3) + (x << 1) + s - '0';s = getchar();}
    return x * sign;
}//快读
inline void write(ll x){
    if(x < 0) putchar('-'),x = -x;
    if(x / 10) write(x / 10);
    putchar(x % 10 + '0');
}
int n,m,vs,ve;
int head[limit],cnt;
int dist[limit],fa[limit],vis[limit],pre[limit];
struct node{
    int to, next, flow, w;
}edge[limit<<1];
void add_one(int u, int v, int flow, int w){
    edge[cnt].to = v;
    edge[cnt].flow = flow;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}
void add(int u, int v, int flow ,int w){
    add_one(u,v,flow,w);
    add_one(v,u,0,-w);
}
void init(int flag = 0){
    if(flag){
        memset(dist, INF, sizeof(dist));
        memset(vis, 0 , sizeof(vis));
    }else{
        memset(head, -1, sizeof(head));
        cnt = 0;
    }
}
int spfa(){
    queue<int>q;
    init(1);
    dist[vs] = 0;
    vis[vs] = 1;
    q.push(vs);
    pre[ve] = -1;
    while (q.size()){
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = head[u]; ~i; i = edge[i].next){
            int v = edge[i].to, w = edge[i].w;
            int stream = edge[i].flow;
            if(dist[u] + w < dist[v] && stream > 0){
                dist[v] = dist[u] + w;
                pre[v] = i;
                fa[v] = u;
                if(!vis[v]){
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    return ~pre[ve];
}

int max_flow,min_cost;
void dinic(){
    while (spfa()){
        int min_flow = INF;
        for(int i = ve; i != vs; i = fa[i]){
            min_flow = min(min_flow, edge[pre[i]].flow);
        }
        max_flow += min_flow;
        min_cost  += dist[ve] * min_flow;
        for(int i = ve; i != vs; i = fa[i]){
            edge[pre[i]].flow -= min_flow;
            edge[pre[i] ^ 1].flow += min_flow;
        }
    }
}
int a[limit];
int main() {
#ifdef LOCAL
    FOPEN;
#endif
    init();
    vs = 40001, ve = vs + 1;
    n = read(), m = read();
    rep(i ,1,n){
        a[i] = read();
    }
    rep(i, 1 ,m){
        int x = read(), y = read(), w = read();
        add(x,y + n,INF,w);
    }
    rep(i ,1, n){
        add(i, i + n , INF, a[i]);//自己添加自己的边
        add(i + n, i , INF, 0);
        add(vs, i , 1, 0);
        add(i + n , ve , 1 , 0);
    }
    dinic();
    write(min_cost);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值