找对象 ssl2637 费用流

18 篇文章 0 订阅

Desciption


这里写图片描述

Solution


一眼题,求费用流的最小割
然后实现用了两个晚上,我果然还是太弱了。。

这里需要说明的是,那些幸福度为0的情侣是不能连边的,不然会作为一条占用流量的无用边
然后这里的每个男孩只能爱一个女孩,那么也不能暴力枚举删哪条边了,
这是一个稠密图,那么只能上zkw过了

以上

Code


#include <stdio.h>
#include <string.h>
#define rep(i, st, ed) for (int i = st; i <= ed; i += 1)
#define erg(i, st) for (int i = ls[st]; i; i = e[i].next)
#define fill(x, t) memset(x, t, sizeof(x))
#define min(x, y) (x)<(y)?(x):(y)
#define INF 0x3f3f3f3f
#define N 201
#define E N * N + 1
struct edge{int x, y, w, c, next;}e[E];
int lim[E], ls[N];
inline void addEdge(int &cnt, int x, int y, int w, int c){
    cnt += 1; e[cnt] = (edge){x, y, w, c, ls[x]}; ls[x] = cnt; lim[cnt] = w;
    cnt += 1; e[cnt] = (edge){y, x, 0, -c, ls[y]}; ls[y] = cnt; lim[cnt] = 0;
    // printf("%d %d %d \n", x, y, c);
}
int vis[N], dis[N], nex[N];
int cost;
inline int find(int now, int ed, int mn){
    if (now == ed || !mn){
        return mn;
    }
    int ret = 0;
    vis[now] = 1;
    erg(i, now){
        if (e[i].w > 0 && dis[e[i].y] + e[i].c == dis[now] && !vis[e[i].y]){
            int d = find(e[i].y, ed, min(mn - ret, e[i].w));
            if (d){
                nex[e[i].x] = i;
            }
            cost += d * e[i].c;
            e[i].w -= d;
            e[i ^ 1].w += d;
            ret += d;
            if (ret == mn){
                break;
            }
        }
    }
    return ret;
}
inline int modify(int st, int ed){
    int mn = INF, inf = INF;
    rep(now, st, ed){
        if (vis[now]){
            erg(i, now){
                if (!vis[e[i].y] && e[i].w > 0){
                    mn = min(mn, dis[e[i].y] + e[i].c - dis[now]);
                }
            }
        }
    }
    if (mn == inf){
        return 0;
    }
    rep(i, st, ed){
        if (vis[i]){
            dis[i] += mn;
        }
    }
    return 1;
}
inline void zkw(int st, int ed){
    fill(dis, 0);
    do{
        rep(i, st, ed){
            vis[i] = 0;
        }
        while (find(st, ed, INF)){
            rep(i, st, ed){
                vis[i] = 0;
            }
        }
    } while (modify(st, ed));
}
int rec[N];
int main(void){
    int n;
    scanf("%d", &n);
    int edgeCnt = 1;
    int st = 0, ed = n * 2 + 1;
    rep(i, 1, n){
        rep(j, 1, n){
            int tmp;
            scanf("%d", &tmp);
            if (tmp){
                addEdge(edgeCnt, i, j + n, 1, -tmp);
            }
        }
        addEdge(edgeCnt, st, i, 1, 0);
        addEdge(edgeCnt, i + n, ed, 1, 0);
    }
    zkw(st, ed);
    rep(i, 1, n){
        rec[i] = nex[i];
    }
    int ans = cost;
    printf("%d\n", -cost);
    rep(i, 1, n){
        rep(j, 2, edgeCnt){e[j].w = lim[j];}
        e[rec[i]].w = e[rec[i] ^ 1].w = cost = 0;
        zkw(st, ed);
        if (ans < cost){
            printf("%d %d\n", i, e[rec[i]].y - n);
        }
    }
    return 0;
}c
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值